1use super::ast::*;
6
7#[derive(Debug, Clone)]
9pub struct PureRenameResult {
10 pub count: usize,
12 pub old_name: String,
14 pub new_name: String,
16}
17
18pub struct PureRename;
20
21impl PureRename {
22 pub fn apply(file: &mut PureFile, old_name: &str, new_name: &str) -> PureRenameResult {
30 let mut renamer = SymbolRenamer::new(old_name, new_name);
31 renamer.visit_file(file);
32
33 PureRenameResult {
34 count: renamer.count,
35 old_name: old_name.to_string(),
36 new_name: new_name.to_string(),
37 }
38 }
39
40 pub fn rename_local_in_fn(
42 file: &mut PureFile,
43 fn_name: &str,
44 old_name: &str,
45 new_name: &str,
46 ) -> PureRenameResult {
47 let mut renamer = ScopedRenamer::new(fn_name, old_name, new_name);
48 renamer.visit_file(file);
49
50 PureRenameResult {
51 count: renamer.count,
52 old_name: old_name.to_string(),
53 new_name: new_name.to_string(),
54 }
55 }
56
57 pub fn apply_cow(
59 file: &PureFile,
60 old_name: &str,
61 new_name: &str,
62 ) -> (PureFile, PureRenameResult) {
63 let mut cloned = file.clone();
64 let result = Self::apply(&mut cloned, old_name, new_name);
65 (cloned, result)
66 }
67}
68
69struct SymbolRenamer {
71 old_name: String,
72 new_name: String,
73 count: usize,
74}
75
76impl SymbolRenamer {
77 fn new(old_name: &str, new_name: &str) -> Self {
78 Self {
79 old_name: old_name.to_string(),
80 new_name: new_name.to_string(),
81 count: 0,
82 }
83 }
84
85 fn maybe_rename(&mut self, name: &mut String) -> bool {
86 if *name == self.old_name {
87 *name = self.new_name.clone();
88 self.count += 1;
89 true
90 } else {
91 false
92 }
93 }
94
95 fn visit_file(&mut self, file: &mut PureFile) {
96 for item in &mut file.items {
97 self.visit_item(item);
98 }
99 }
100
101 fn visit_item(&mut self, item: &mut PureItem) {
102 match item {
103 PureItem::Fn(f) => self.visit_fn(f),
104 PureItem::Struct(s) => self.visit_struct(s),
105 PureItem::Enum(e) => self.visit_enum(e),
106 PureItem::Impl(i) => self.visit_impl(i),
107 PureItem::Trait(t) => self.visit_trait(t),
108 PureItem::Const(c) => self.visit_const(c),
109 PureItem::Static(s) => self.visit_static(s),
110 PureItem::Type(t) => self.visit_type_alias(t),
111 PureItem::Mod(m) => self.visit_mod(m),
112 PureItem::Use(u) => self.visit_use(u),
113 PureItem::Macro(_) | PureItem::Other(_) | PureItem::Verbatim(_) => {}
114 }
115 }
116
117 fn visit_fn(&mut self, f: &mut PureFn) {
118 self.maybe_rename(&mut f.name);
119
120 for param in &mut f.params {
122 if let PureParam::Typed { name, ty, .. } = param {
123 self.maybe_rename(name);
124 self.visit_type(ty);
125 }
126 }
127
128 if let Some(ty) = &mut f.ret {
130 self.visit_type(ty);
131 }
132
133 self.visit_block(&mut f.body);
135 }
136
137 fn visit_struct(&mut self, s: &mut PureStruct) {
138 self.maybe_rename(&mut s.name);
139 self.visit_fields(&mut s.fields);
140 }
141
142 fn visit_fields(&mut self, fields: &mut PureFields) {
143 match fields {
144 PureFields::Named(named) => {
145 for field in named {
146 self.visit_type(&mut field.ty);
147 }
148 }
149 PureFields::Tuple(fields) => {
150 for f in fields {
151 self.visit_type(&mut f.ty);
152 }
153 }
154 PureFields::Unit => {}
155 }
156 }
157
158 fn visit_enum(&mut self, e: &mut PureEnum) {
159 self.maybe_rename(&mut e.name);
160 for variant in &mut e.variants {
161 self.visit_fields(&mut variant.fields);
162 }
163 }
164
165 fn visit_impl(&mut self, i: &mut PureImpl) {
166 self.maybe_rename(&mut i.self_ty);
167 if let Some(trait_name) = &mut i.trait_ {
168 self.maybe_rename(trait_name);
169 }
170 for item in &mut i.items {
171 if let PureImplItem::Fn(f) = item {
172 self.visit_fn(f);
173 }
174 }
175 }
176
177 fn visit_trait(&mut self, t: &mut PureTrait) {
178 self.maybe_rename(&mut t.name);
179 }
180
181 fn visit_const(&mut self, c: &mut PureConst) {
182 self.maybe_rename(&mut c.name);
183 self.visit_type(&mut c.ty);
184 if let Some(v) = &mut c.value {
185 self.visit_expr(v);
186 }
187 }
188
189 fn visit_static(&mut self, s: &mut PureStatic) {
190 self.maybe_rename(&mut s.name);
191 self.visit_type(&mut s.ty);
192 self.visit_expr(&mut s.value);
193 }
194
195 fn visit_type_alias(&mut self, t: &mut PureTypeAlias) {
196 self.maybe_rename(&mut t.name);
197 self.visit_type(&mut t.ty);
198 }
199
200 fn visit_mod(&mut self, m: &mut PureMod) {
201 self.maybe_rename(&mut m.name);
202 for item in &mut m.items {
203 self.visit_item(item);
204 }
205 }
206
207 fn visit_use(&mut self, u: &mut PureUse) {
208 self.visit_use_tree(&mut u.tree);
209 }
210
211 fn visit_use_tree(&mut self, tree: &mut PureUseTree) {
212 match tree {
213 PureUseTree::Path { tree, .. } => {
214 self.visit_use_tree(tree);
215 }
216 PureUseTree::Name(name) => {
217 self.maybe_rename(name);
218 }
219 PureUseTree::Rename { name, .. } => {
220 self.maybe_rename(name);
221 }
222 PureUseTree::Glob => {}
223 PureUseTree::Group(trees) => {
224 for t in trees {
225 self.visit_use_tree(t);
226 }
227 }
228 }
229 }
230
231 fn visit_type(&mut self, ty: &mut PureType) {
232 match ty {
233 PureType::Path(path) => {
234 self.maybe_rename_qualified_path(path);
236 }
237 PureType::Ref { ty, .. } => {
238 self.visit_type(ty);
239 }
240 PureType::Slice(ty) => {
241 self.visit_type(ty);
242 }
243 PureType::Array { ty, .. } => {
244 self.visit_type(ty);
245 }
246 PureType::Tuple(types) => {
247 for ty in types {
248 self.visit_type(ty);
249 }
250 }
251 PureType::Fn { params, ret } => {
252 for ty in params {
253 self.visit_type(ty);
254 }
255 if let Some(ret_ty) = ret {
256 self.visit_type(ret_ty);
257 }
258 }
259 PureType::ImplTrait(bounds) => {
260 for bound in bounds {
261 self.maybe_rename_qualified_path(bound);
262 }
263 }
264 PureType::TraitObject(bounds) => {
265 for bound in bounds {
266 self.maybe_rename_qualified_path(bound);
267 }
268 }
269 PureType::Infer | PureType::Never | PureType::Other(_) => {}
270 }
271 }
272
273 fn visit_block(&mut self, block: &mut PureBlock) {
274 for stmt in &mut block.stmts {
275 self.visit_stmt(stmt);
276 }
277 }
278
279 fn visit_stmt(&mut self, stmt: &mut PureStmt) {
280 match stmt {
281 PureStmt::Local {
282 pattern,
283 init,
284 ty,
285 else_branch,
286 } => {
287 self.visit_pattern(pattern);
288 if let Some(ty) = ty {
289 self.visit_type(ty);
290 }
291 if let Some(expr) = init {
292 self.visit_expr(expr);
293 }
294 if let Some(eb) = else_branch {
295 self.visit_expr(eb);
296 }
297 }
298 PureStmt::Expr(expr) | PureStmt::Semi(expr) => {
299 self.visit_expr(expr);
300 }
301 PureStmt::Item(item) => {
302 self.visit_item(item);
303 }
304 PureStmt::Verbatim(_) => {}
307 }
308 }
309
310 fn visit_pattern(&mut self, pattern: &mut PurePattern) {
311 match pattern {
312 PurePattern::Ident { name, .. } => {
313 self.maybe_rename(name);
314 }
315 PurePattern::Tuple(pats) => {
316 for pat in pats {
317 self.visit_pattern(pat);
318 }
319 }
320 PurePattern::Struct { path, fields, .. } => {
321 self.maybe_rename_qualified_path(path);
322 for (_, pat) in fields {
323 self.visit_pattern(pat);
324 }
325 }
326 PurePattern::Ref { pattern, .. } => {
327 self.visit_pattern(pattern);
328 }
329 PurePattern::Or(pats) => {
330 for pat in pats {
331 self.visit_pattern(pat);
332 }
333 }
334 PurePattern::Slice(pats) => {
335 for pat in pats {
336 self.visit_pattern(pat);
337 }
338 }
339 PurePattern::Path(path) => {
340 self.maybe_rename_qualified_path(path);
343 }
344 PurePattern::Wild
345 | PurePattern::Lit(_)
346 | PurePattern::Range { .. }
347 | PurePattern::Rest
348 | PurePattern::Other(_) => {}
349 }
350 }
351
352 fn maybe_rename_qualified_path(&mut self, path: &mut String) {
356 if path.contains("::") {
357 if let Some(first) = path.split("::").next() {
359 if first == self.old_name {
360 let rest = &path[first.len()..];
361 *path = format!("{}{}", self.new_name, rest);
362 self.count += 1;
363 return;
364 }
365 }
366 if let Some(last) = path.rsplit("::").next() {
368 if last == self.old_name {
369 if let Some(sep_idx) = path.rfind("::") {
370 let prefix = &path[..sep_idx + 2];
371 *path = format!("{}{}", prefix, self.new_name);
372 self.count += 1;
373 } else {
374 *path = self.new_name.to_string();
376 self.count += 1;
377 }
378 }
379 }
380 } else {
381 self.maybe_rename(path);
382 }
383 }
384
385 fn visit_expr(&mut self, expr: &mut PureExpr) {
386 match expr {
387 PureExpr::Path(path) => {
388 self.maybe_rename_qualified_path(path);
389 }
390 PureExpr::Binary { left, right, .. } => {
391 self.visit_expr(left);
392 self.visit_expr(right);
393 }
394 PureExpr::Unary { expr, .. } => {
395 self.visit_expr(expr);
396 }
397 PureExpr::Call { func, args } => {
398 self.visit_expr(func);
399 for arg in args {
400 self.visit_expr(arg);
401 }
402 }
403 PureExpr::MethodCall { receiver, args, .. } => {
404 self.visit_expr(receiver);
405 for arg in args {
406 self.visit_expr(arg);
407 }
408 }
409 PureExpr::Field { expr, .. } => {
410 self.visit_expr(expr);
411 }
412 PureExpr::Index { expr, index } => {
413 self.visit_expr(expr);
414 self.visit_expr(index);
415 }
416 PureExpr::Block { block, .. } => {
417 self.visit_block(block);
418 }
419 PureExpr::If {
420 cond,
421 then_branch,
422 else_branch,
423 } => {
424 self.visit_expr(cond);
425 self.visit_block(then_branch);
426 if let Some(else_expr) = else_branch {
427 self.visit_expr(else_expr);
428 }
429 }
430 PureExpr::Match { expr, arms } => {
431 self.visit_expr(expr);
432 for arm in arms {
433 self.visit_pattern(&mut arm.pattern);
434 if let Some(guard) = &mut arm.guard {
435 self.visit_expr(guard);
436 }
437 self.visit_expr(&mut arm.body);
438 }
439 }
440 PureExpr::Loop { body: block, .. } => {
441 self.visit_block(block);
442 }
443 PureExpr::While { cond, body, .. } => {
444 self.visit_expr(cond);
445 self.visit_block(body);
446 }
447 PureExpr::For {
448 pat, expr, body, ..
449 } => {
450 self.visit_expr(expr);
451 self.visit_pattern(pat);
452 self.visit_block(body);
453 }
454 PureExpr::Return(Some(expr))
455 | PureExpr::Break {
456 expr: Some(expr), ..
457 } => {
458 self.visit_expr(expr);
459 }
460 PureExpr::Closure { params, body, .. } => {
461 for param in params {
462 self.visit_pattern(&mut param.pattern);
463 }
464 self.visit_expr(body);
465 }
466 PureExpr::Struct { path, fields, rest } => {
467 if !path.contains("::") {
468 self.maybe_rename(path);
469 }
470 for (_, expr) in fields {
471 self.visit_expr(expr);
472 }
473 if let Some(r) = rest {
474 self.visit_expr(r);
475 }
476 }
477 PureExpr::Tuple(exprs) | PureExpr::Array(exprs) => {
478 for expr in exprs {
479 self.visit_expr(expr);
480 }
481 }
482 PureExpr::Ref { expr, .. } => {
483 self.visit_expr(expr);
484 }
485 PureExpr::Await(expr) | PureExpr::Try(expr) => {
486 self.visit_expr(expr);
487 }
488 PureExpr::Macro { tokens, .. } => {
489 self.rename_in_tokens(tokens);
491 }
492 _ => {}
493 }
494 }
495
496 fn rename_in_tokens(&mut self, tokens: &mut String) {
499 let qualified_pattern = format!("{}::", self.old_name);
505 let qualified_replacement = format!("{}::", self.new_name);
506 if tokens.contains(&qualified_pattern) {
507 let count = tokens.matches(&qualified_pattern).count();
508 *tokens = tokens.replace(&qualified_pattern, &qualified_replacement);
509 self.count += count;
510 }
511
512 let mut new_tokens = String::with_capacity(tokens.len());
515 let mut last_end = 0;
516 let old_bytes = self.old_name.as_bytes();
517 let token_bytes = tokens.as_bytes();
518
519 let mut i = 0;
520 while i <= token_bytes.len().saturating_sub(old_bytes.len()) {
521 if &token_bytes[i..i + old_bytes.len()] == old_bytes {
522 let is_word_start = i == 0 || !is_ident_char(token_bytes[i - 1] as char);
524 let is_word_end = i + old_bytes.len() >= token_bytes.len()
526 || !is_ident_char(token_bytes[i + old_bytes.len()] as char);
527
528 let is_qualified = i + old_bytes.len() < token_bytes.len()
530 && token_bytes[i + old_bytes.len()] == b':';
531
532 if is_word_start && is_word_end && !is_qualified {
533 new_tokens.push_str(&tokens[last_end..i]);
534 new_tokens.push_str(&self.new_name);
535 last_end = i + old_bytes.len();
536 self.count += 1;
537 i += old_bytes.len();
538 continue;
539 }
540 }
541 i += 1;
542 }
543 new_tokens.push_str(&tokens[last_end..]);
544 *tokens = new_tokens;
545 }
546}
547
548fn is_ident_char(c: char) -> bool {
550 c.is_alphanumeric() || c == '_'
551}
552
553struct ScopedRenamer {
555 target_fn: String,
556 old_name: String,
557 new_name: String,
558 count: usize,
559 in_target_fn: bool,
560}
561
562impl ScopedRenamer {
563 fn new(target_fn: &str, old_name: &str, new_name: &str) -> Self {
564 Self {
565 target_fn: target_fn.to_string(),
566 old_name: old_name.to_string(),
567 new_name: new_name.to_string(),
568 count: 0,
569 in_target_fn: false,
570 }
571 }
572
573 fn maybe_rename(&mut self, name: &mut String) {
574 if self.in_target_fn && *name == self.old_name {
575 *name = self.new_name.clone();
576 self.count += 1;
577 }
578 }
579
580 fn visit_file(&mut self, file: &mut PureFile) {
581 for item in &mut file.items {
582 self.visit_item(item);
583 }
584 }
585
586 fn visit_item(&mut self, item: &mut PureItem) {
587 if let PureItem::Fn(f) = item {
588 let was_in_target = self.in_target_fn;
589
590 if f.name == self.target_fn {
591 self.in_target_fn = true;
592 }
593
594 self.visit_fn(f);
595
596 self.in_target_fn = was_in_target;
597 }
598 }
599
600 fn visit_fn(&mut self, f: &mut PureFn) {
601 for param in &mut f.params {
603 if let PureParam::Typed { name, .. } = param {
604 self.maybe_rename(name);
605 }
606 }
607
608 self.visit_block(&mut f.body);
610 }
611
612 fn visit_block(&mut self, block: &mut PureBlock) {
613 for stmt in &mut block.stmts {
614 self.visit_stmt(stmt);
615 }
616 }
617
618 fn visit_stmt(&mut self, stmt: &mut PureStmt) {
619 match stmt {
620 PureStmt::Local { pattern, init, .. } => {
621 self.visit_pattern(pattern);
622 if let Some(expr) = init {
623 self.visit_expr(expr);
624 }
625 }
626 PureStmt::Expr(expr) | PureStmt::Semi(expr) => {
627 self.visit_expr(expr);
628 }
629 PureStmt::Item(_) => {}
630 PureStmt::Verbatim(_) => {}
633 }
634 }
635
636 fn visit_pattern(&mut self, pattern: &mut PurePattern) {
637 match pattern {
638 PurePattern::Ident { name, .. } => {
639 self.maybe_rename(name);
640 }
641 PurePattern::Tuple(pats) => {
642 for pat in pats {
643 self.visit_pattern(pat);
644 }
645 }
646 PurePattern::Struct { fields, .. } => {
647 for (_, pat) in fields {
648 self.visit_pattern(pat);
649 }
650 }
651 PurePattern::Ref { pattern, .. } => {
652 self.visit_pattern(pattern);
653 }
654 PurePattern::Or(pats) => {
655 for pat in pats {
656 self.visit_pattern(pat);
657 }
658 }
659 PurePattern::Slice(pats) => {
660 for pat in pats {
661 self.visit_pattern(pat);
662 }
663 }
664 PurePattern::Path(path) => {
665 if let Some(last) = path.rsplit("::").next() {
667 if last == self.old_name {
668 if let Some(sep_idx) = path.rfind("::") {
669 let prefix = &path[..sep_idx + 2];
670 *path = format!("{}{}", prefix, self.new_name);
671 } else {
672 *path = self.new_name.to_string();
673 }
674 }
675 }
676 }
677 PurePattern::Wild
678 | PurePattern::Lit(_)
679 | PurePattern::Range { .. }
680 | PurePattern::Rest
681 | PurePattern::Other(_) => {}
682 }
683 }
684
685 fn visit_expr(&mut self, expr: &mut PureExpr) {
686 match expr {
687 PureExpr::Path(path) if !path.contains("::") => {
688 self.maybe_rename(path);
689 }
690 PureExpr::Binary { left, right, .. } => {
691 self.visit_expr(left);
692 self.visit_expr(right);
693 }
694 PureExpr::Unary { expr, .. } => {
695 self.visit_expr(expr);
696 }
697 PureExpr::Call { func, args } => {
698 self.visit_expr(func);
699 for arg in args {
700 self.visit_expr(arg);
701 }
702 }
703 PureExpr::MethodCall { receiver, args, .. } => {
704 self.visit_expr(receiver);
705 for arg in args {
706 self.visit_expr(arg);
707 }
708 }
709 PureExpr::Field { expr, .. } => {
710 self.visit_expr(expr);
711 }
712 PureExpr::Index { expr, index } => {
713 self.visit_expr(expr);
714 self.visit_expr(index);
715 }
716 PureExpr::Block { block, .. } => {
717 self.visit_block(block);
718 }
719 PureExpr::If {
720 cond,
721 then_branch,
722 else_branch,
723 } => {
724 self.visit_expr(cond);
725 self.visit_block(then_branch);
726 if let Some(else_expr) = else_branch {
727 self.visit_expr(else_expr);
728 }
729 }
730 PureExpr::Match { expr, arms } => {
731 self.visit_expr(expr);
732 for arm in arms {
733 self.visit_pattern(&mut arm.pattern);
734 if let Some(guard) = &mut arm.guard {
735 self.visit_expr(guard);
736 }
737 self.visit_expr(&mut arm.body);
738 }
739 }
740 PureExpr::Loop { body: block, .. } => {
741 self.visit_block(block);
742 }
743 PureExpr::While { cond, body, .. } => {
744 self.visit_expr(cond);
745 self.visit_block(body);
746 }
747 PureExpr::For {
748 pat, expr, body, ..
749 } => {
750 self.visit_expr(expr);
751 self.visit_pattern(pat);
752 self.visit_block(body);
753 }
754 PureExpr::Return(Some(expr))
755 | PureExpr::Break {
756 expr: Some(expr), ..
757 } => {
758 self.visit_expr(expr);
759 }
760 PureExpr::Closure { params, body, .. } => {
761 for param in params {
762 self.visit_pattern(&mut param.pattern);
763 }
764 self.visit_expr(body);
765 }
766 PureExpr::Struct { fields, .. } => {
767 for (_, expr) in fields {
768 self.visit_expr(expr);
769 }
770 }
771 PureExpr::Tuple(exprs) | PureExpr::Array(exprs) => {
772 for expr in exprs {
773 self.visit_expr(expr);
774 }
775 }
776 PureExpr::Ref { expr, .. } => {
777 self.visit_expr(expr);
778 }
779 PureExpr::Await(expr) | PureExpr::Try(expr) => {
780 self.visit_expr(expr);
781 }
782 _ => {}
783 }
784 }
785}
786
787#[cfg(test)]
788mod tests {
789 use super::*;
790
791 #[test]
792 fn test_rename_local_var() {
793 let mut file = PureFile::from_source(
794 r#"
795 fn main() {
796 let x = 1;
797 let y = x + 1;
798 }
799 "#,
800 )
801 .unwrap();
802
803 let result = PureRename::apply(&mut file, "x", "value");
804 assert_eq!(result.count, 2); let f = file.functions().into_iter().next().unwrap();
808 assert!(f.body.stmts.iter().any(|stmt| {
809 if let PureStmt::Local {
810 pattern: PurePattern::Ident { name, .. },
811 ..
812 } = stmt
813 {
814 name == "value"
815 } else {
816 false
817 }
818 }));
819 }
820
821 #[test]
822 fn test_rename_function() {
823 let mut file = PureFile::from_source(
824 r#"
825 fn foo() {}
826 fn main() {
827 foo();
828 }
829 "#,
830 )
831 .unwrap();
832
833 let result = PureRename::apply(&mut file, "foo", "bar");
834 assert_eq!(result.count, 2); let fns = file.functions();
837 assert!(fns.iter().any(|f| f.name == "bar"));
838 assert!(!fns.iter().any(|f| f.name == "foo"));
839 }
840
841 #[test]
842 fn test_rename_struct() {
843 let mut file = PureFile::from_source(
844 r#"
845 struct Point { x: i32, y: i32 }
846 fn main() {
847 let p: Point = Point { x: 0, y: 0 };
848 }
849 "#,
850 )
851 .unwrap();
852
853 let result = PureRename::apply(&mut file, "Point", "Vec2");
854 assert!(result.count >= 2); let structs = file.structs();
857 assert!(structs.iter().any(|s| s.name == "Vec2"));
858 assert!(!structs.iter().any(|s| s.name == "Point"));
859 }
860
861 #[test]
862 fn test_scoped_rename() {
863 let mut file = PureFile::from_source(
864 r#"
865 fn foo() {
866 let x = 1;
867 }
868 fn bar() {
869 let x = 2;
870 }
871 "#,
872 )
873 .unwrap();
874
875 let result = PureRename::rename_local_in_fn(&mut file, "foo", "x", "renamed");
876 assert_eq!(result.count, 1);
877
878 let foo = file
880 .functions()
881 .into_iter()
882 .find(|f| f.name == "foo")
883 .unwrap();
884 assert!(foo.body.stmts.iter().any(|stmt| {
885 if let PureStmt::Local {
886 pattern: PurePattern::Ident { name, .. },
887 ..
888 } = stmt
889 {
890 name == "renamed"
891 } else {
892 false
893 }
894 }));
895
896 let bar = file
898 .functions()
899 .into_iter()
900 .find(|f| f.name == "bar")
901 .unwrap();
902 assert!(bar.body.stmts.iter().any(|stmt| {
903 if let PureStmt::Local {
904 pattern: PurePattern::Ident { name, .. },
905 ..
906 } = stmt
907 {
908 name == "x"
909 } else {
910 false
911 }
912 }));
913 }
914
915 #[test]
916 fn test_cow_rename() {
917 let original = PureFile::from_source("fn foo() {}").unwrap();
918
919 let (renamed, result) = PureRename::apply_cow(&original, "foo", "bar");
920
921 assert_eq!(result.count, 1);
922
923 assert!(original.functions().iter().any(|f| f.name == "foo"));
925
926 assert!(renamed.functions().iter().any(|f| f.name == "bar"));
928 }
929
930 #[test]
931 fn test_parallel_rename() {
932 use std::sync::Arc;
933 use std::thread;
934
935 let file = PureFile::from_source(
936 r#"
937 fn alpha() {}
938 fn beta() {}
939 fn gamma() {}
940 "#,
941 )
942 .unwrap();
943
944 let shared = Arc::new(file);
945
946 let handles: Vec<_> = vec!["alpha", "beta", "gamma"]
948 .into_iter()
949 .map(|old_name| {
950 let f = Arc::clone(&shared);
951 let new_name = format!("{}_renamed", old_name);
952 thread::spawn(move || {
953 let (renamed, result) = PureRename::apply_cow(&f, old_name, &new_name);
954 (renamed.functions().len(), result.count)
955 })
956 })
957 .collect();
958
959 for handle in handles {
960 let (fn_count, rename_count) = handle.join().unwrap();
961 assert_eq!(fn_count, 3);
962 assert_eq!(rename_count, 1);
963 }
964 }
965}