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(_) => {}
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(types) => {
150 for ty in types {
151 self.visit_type(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 { pattern, init, ty } => {
282 self.visit_pattern(pattern);
283 if let Some(ty) = ty {
284 self.visit_type(ty);
285 }
286 if let Some(expr) = init {
287 self.visit_expr(expr);
288 }
289 }
290 PureStmt::Expr(expr) | PureStmt::Semi(expr) => {
291 self.visit_expr(expr);
292 }
293 PureStmt::Item(item) => {
294 self.visit_item(item);
295 }
296 }
297 }
298
299 fn visit_pattern(&mut self, pattern: &mut PurePattern) {
300 match pattern {
301 PurePattern::Ident { name, .. } => {
302 self.maybe_rename(name);
303 }
304 PurePattern::Tuple(pats) => {
305 for pat in pats {
306 self.visit_pattern(pat);
307 }
308 }
309 PurePattern::Struct { path, fields, .. } => {
310 self.maybe_rename_qualified_path(path);
311 for (_, pat) in fields {
312 self.visit_pattern(pat);
313 }
314 }
315 PurePattern::Ref { pattern, .. } => {
316 self.visit_pattern(pattern);
317 }
318 PurePattern::Or(pats) => {
319 for pat in pats {
320 self.visit_pattern(pat);
321 }
322 }
323 PurePattern::Slice(pats) => {
324 for pat in pats {
325 self.visit_pattern(pat);
326 }
327 }
328 PurePattern::Path(path) => {
329 self.maybe_rename_qualified_path(path);
332 }
333 PurePattern::Wild
334 | PurePattern::Lit(_)
335 | PurePattern::Range { .. }
336 | PurePattern::Rest
337 | PurePattern::Other(_) => {}
338 }
339 }
340
341 fn maybe_rename_qualified_path(&mut self, path: &mut String) {
345 if path.contains("::") {
346 if let Some(first) = path.split("::").next() {
348 if first == self.old_name {
349 let rest = &path[first.len()..];
350 *path = format!("{}{}", self.new_name, rest);
351 self.count += 1;
352 return;
353 }
354 }
355 if let Some(last) = path.rsplit("::").next() {
357 if last == self.old_name {
358 if let Some(sep_idx) = path.rfind("::") {
359 let prefix = &path[..sep_idx + 2];
360 *path = format!("{}{}", prefix, self.new_name);
361 self.count += 1;
362 } else {
363 *path = self.new_name.to_string();
365 self.count += 1;
366 }
367 }
368 }
369 } else {
370 self.maybe_rename(path);
371 }
372 }
373
374 fn visit_expr(&mut self, expr: &mut PureExpr) {
375 match expr {
376 PureExpr::Path(path) => {
377 self.maybe_rename_qualified_path(path);
378 }
379 PureExpr::Binary { left, right, .. } => {
380 self.visit_expr(left);
381 self.visit_expr(right);
382 }
383 PureExpr::Unary { expr, .. } => {
384 self.visit_expr(expr);
385 }
386 PureExpr::Call { func, args } => {
387 self.visit_expr(func);
388 for arg in args {
389 self.visit_expr(arg);
390 }
391 }
392 PureExpr::MethodCall { receiver, args, .. } => {
393 self.visit_expr(receiver);
394 for arg in args {
395 self.visit_expr(arg);
396 }
397 }
398 PureExpr::Field { expr, .. } => {
399 self.visit_expr(expr);
400 }
401 PureExpr::Index { expr, index } => {
402 self.visit_expr(expr);
403 self.visit_expr(index);
404 }
405 PureExpr::Block { block, .. } => {
406 self.visit_block(block);
407 }
408 PureExpr::If {
409 cond,
410 then_branch,
411 else_branch,
412 } => {
413 self.visit_expr(cond);
414 self.visit_block(then_branch);
415 if let Some(else_expr) = else_branch {
416 self.visit_expr(else_expr);
417 }
418 }
419 PureExpr::Match { expr, arms } => {
420 self.visit_expr(expr);
421 for arm in arms {
422 self.visit_pattern(&mut arm.pattern);
423 if let Some(guard) = &mut arm.guard {
424 self.visit_expr(guard);
425 }
426 self.visit_expr(&mut arm.body);
427 }
428 }
429 PureExpr::Loop { body: block, .. } => {
430 self.visit_block(block);
431 }
432 PureExpr::While { cond, body, .. } => {
433 self.visit_expr(cond);
434 self.visit_block(body);
435 }
436 PureExpr::For {
437 pat, expr, body, ..
438 } => {
439 self.visit_expr(expr);
440 self.visit_pattern(pat);
441 self.visit_block(body);
442 }
443 PureExpr::Return(Some(expr))
444 | PureExpr::Break {
445 expr: Some(expr), ..
446 } => {
447 self.visit_expr(expr);
448 }
449 PureExpr::Closure { params, body, .. } => {
450 for param in params {
451 self.visit_pattern(&mut param.pattern);
452 }
453 self.visit_expr(body);
454 }
455 PureExpr::Struct { path, fields } => {
456 if !path.contains("::") {
457 self.maybe_rename(path);
458 }
459 for (_, expr) in fields {
460 self.visit_expr(expr);
461 }
462 }
463 PureExpr::Tuple(exprs) | PureExpr::Array(exprs) => {
464 for expr in exprs {
465 self.visit_expr(expr);
466 }
467 }
468 PureExpr::Ref { expr, .. } => {
469 self.visit_expr(expr);
470 }
471 PureExpr::Await(expr) | PureExpr::Try(expr) => {
472 self.visit_expr(expr);
473 }
474 PureExpr::Macro { tokens, .. } => {
475 self.rename_in_tokens(tokens);
477 }
478 _ => {}
479 }
480 }
481
482 fn rename_in_tokens(&mut self, tokens: &mut String) {
485 let qualified_pattern = format!("{}::", self.old_name);
491 let qualified_replacement = format!("{}::", self.new_name);
492 if tokens.contains(&qualified_pattern) {
493 let count = tokens.matches(&qualified_pattern).count();
494 *tokens = tokens.replace(&qualified_pattern, &qualified_replacement);
495 self.count += count;
496 }
497
498 let mut new_tokens = String::with_capacity(tokens.len());
501 let mut last_end = 0;
502 let old_bytes = self.old_name.as_bytes();
503 let token_bytes = tokens.as_bytes();
504
505 let mut i = 0;
506 while i <= token_bytes.len().saturating_sub(old_bytes.len()) {
507 if &token_bytes[i..i + old_bytes.len()] == old_bytes {
508 let is_word_start = i == 0 || !is_ident_char(token_bytes[i - 1] as char);
510 let is_word_end = i + old_bytes.len() >= token_bytes.len()
512 || !is_ident_char(token_bytes[i + old_bytes.len()] as char);
513
514 let is_qualified = i + old_bytes.len() < token_bytes.len()
516 && token_bytes[i + old_bytes.len()] == b':';
517
518 if is_word_start && is_word_end && !is_qualified {
519 new_tokens.push_str(&tokens[last_end..i]);
520 new_tokens.push_str(&self.new_name);
521 last_end = i + old_bytes.len();
522 self.count += 1;
523 i += old_bytes.len();
524 continue;
525 }
526 }
527 i += 1;
528 }
529 new_tokens.push_str(&tokens[last_end..]);
530 *tokens = new_tokens;
531 }
532}
533
534fn is_ident_char(c: char) -> bool {
536 c.is_alphanumeric() || c == '_'
537}
538
539struct ScopedRenamer {
541 target_fn: String,
542 old_name: String,
543 new_name: String,
544 count: usize,
545 in_target_fn: bool,
546}
547
548impl ScopedRenamer {
549 fn new(target_fn: &str, old_name: &str, new_name: &str) -> Self {
550 Self {
551 target_fn: target_fn.to_string(),
552 old_name: old_name.to_string(),
553 new_name: new_name.to_string(),
554 count: 0,
555 in_target_fn: false,
556 }
557 }
558
559 fn maybe_rename(&mut self, name: &mut String) {
560 if self.in_target_fn && *name == self.old_name {
561 *name = self.new_name.clone();
562 self.count += 1;
563 }
564 }
565
566 fn visit_file(&mut self, file: &mut PureFile) {
567 for item in &mut file.items {
568 self.visit_item(item);
569 }
570 }
571
572 fn visit_item(&mut self, item: &mut PureItem) {
573 if let PureItem::Fn(f) = item {
574 let was_in_target = self.in_target_fn;
575
576 if f.name == self.target_fn {
577 self.in_target_fn = true;
578 }
579
580 self.visit_fn(f);
581
582 self.in_target_fn = was_in_target;
583 }
584 }
585
586 fn visit_fn(&mut self, f: &mut PureFn) {
587 for param in &mut f.params {
589 if let PureParam::Typed { name, .. } = param {
590 self.maybe_rename(name);
591 }
592 }
593
594 self.visit_block(&mut f.body);
596 }
597
598 fn visit_block(&mut self, block: &mut PureBlock) {
599 for stmt in &mut block.stmts {
600 self.visit_stmt(stmt);
601 }
602 }
603
604 fn visit_stmt(&mut self, stmt: &mut PureStmt) {
605 match stmt {
606 PureStmt::Local { pattern, init, .. } => {
607 self.visit_pattern(pattern);
608 if let Some(expr) = init {
609 self.visit_expr(expr);
610 }
611 }
612 PureStmt::Expr(expr) | PureStmt::Semi(expr) => {
613 self.visit_expr(expr);
614 }
615 PureStmt::Item(_) => {}
616 }
617 }
618
619 fn visit_pattern(&mut self, pattern: &mut PurePattern) {
620 match pattern {
621 PurePattern::Ident { name, .. } => {
622 self.maybe_rename(name);
623 }
624 PurePattern::Tuple(pats) => {
625 for pat in pats {
626 self.visit_pattern(pat);
627 }
628 }
629 PurePattern::Struct { fields, .. } => {
630 for (_, pat) in fields {
631 self.visit_pattern(pat);
632 }
633 }
634 PurePattern::Ref { pattern, .. } => {
635 self.visit_pattern(pattern);
636 }
637 PurePattern::Or(pats) => {
638 for pat in pats {
639 self.visit_pattern(pat);
640 }
641 }
642 PurePattern::Slice(pats) => {
643 for pat in pats {
644 self.visit_pattern(pat);
645 }
646 }
647 PurePattern::Path(path) => {
648 if let Some(last) = path.rsplit("::").next() {
650 if last == self.old_name {
651 if let Some(sep_idx) = path.rfind("::") {
652 let prefix = &path[..sep_idx + 2];
653 *path = format!("{}{}", prefix, self.new_name);
654 } else {
655 *path = self.new_name.to_string();
656 }
657 }
658 }
659 }
660 PurePattern::Wild
661 | PurePattern::Lit(_)
662 | PurePattern::Range { .. }
663 | PurePattern::Rest
664 | PurePattern::Other(_) => {}
665 }
666 }
667
668 fn visit_expr(&mut self, expr: &mut PureExpr) {
669 match expr {
670 PureExpr::Path(path) if !path.contains("::") => {
671 self.maybe_rename(path);
672 }
673 PureExpr::Binary { left, right, .. } => {
674 self.visit_expr(left);
675 self.visit_expr(right);
676 }
677 PureExpr::Unary { expr, .. } => {
678 self.visit_expr(expr);
679 }
680 PureExpr::Call { func, args } => {
681 self.visit_expr(func);
682 for arg in args {
683 self.visit_expr(arg);
684 }
685 }
686 PureExpr::MethodCall { receiver, args, .. } => {
687 self.visit_expr(receiver);
688 for arg in args {
689 self.visit_expr(arg);
690 }
691 }
692 PureExpr::Field { expr, .. } => {
693 self.visit_expr(expr);
694 }
695 PureExpr::Index { expr, index } => {
696 self.visit_expr(expr);
697 self.visit_expr(index);
698 }
699 PureExpr::Block { block, .. } => {
700 self.visit_block(block);
701 }
702 PureExpr::If {
703 cond,
704 then_branch,
705 else_branch,
706 } => {
707 self.visit_expr(cond);
708 self.visit_block(then_branch);
709 if let Some(else_expr) = else_branch {
710 self.visit_expr(else_expr);
711 }
712 }
713 PureExpr::Match { expr, arms } => {
714 self.visit_expr(expr);
715 for arm in arms {
716 self.visit_pattern(&mut arm.pattern);
717 if let Some(guard) = &mut arm.guard {
718 self.visit_expr(guard);
719 }
720 self.visit_expr(&mut arm.body);
721 }
722 }
723 PureExpr::Loop { body: block, .. } => {
724 self.visit_block(block);
725 }
726 PureExpr::While { cond, body, .. } => {
727 self.visit_expr(cond);
728 self.visit_block(body);
729 }
730 PureExpr::For {
731 pat, expr, body, ..
732 } => {
733 self.visit_expr(expr);
734 self.visit_pattern(pat);
735 self.visit_block(body);
736 }
737 PureExpr::Return(Some(expr))
738 | PureExpr::Break {
739 expr: Some(expr), ..
740 } => {
741 self.visit_expr(expr);
742 }
743 PureExpr::Closure { params, body, .. } => {
744 for param in params {
745 self.visit_pattern(&mut param.pattern);
746 }
747 self.visit_expr(body);
748 }
749 PureExpr::Struct { fields, .. } => {
750 for (_, expr) in fields {
751 self.visit_expr(expr);
752 }
753 }
754 PureExpr::Tuple(exprs) | PureExpr::Array(exprs) => {
755 for expr in exprs {
756 self.visit_expr(expr);
757 }
758 }
759 PureExpr::Ref { expr, .. } => {
760 self.visit_expr(expr);
761 }
762 PureExpr::Await(expr) | PureExpr::Try(expr) => {
763 self.visit_expr(expr);
764 }
765 _ => {}
766 }
767 }
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773
774 #[test]
775 fn test_rename_local_var() {
776 let mut file = PureFile::from_source(
777 r#"
778 fn main() {
779 let x = 1;
780 let y = x + 1;
781 }
782 "#,
783 )
784 .unwrap();
785
786 let result = PureRename::apply(&mut file, "x", "value");
787 assert_eq!(result.count, 2); let f = file.functions().into_iter().next().unwrap();
791 assert!(f.body.stmts.iter().any(|stmt| {
792 if let PureStmt::Local {
793 pattern: PurePattern::Ident { name, .. },
794 ..
795 } = stmt
796 {
797 name == "value"
798 } else {
799 false
800 }
801 }));
802 }
803
804 #[test]
805 fn test_rename_function() {
806 let mut file = PureFile::from_source(
807 r#"
808 fn foo() {}
809 fn main() {
810 foo();
811 }
812 "#,
813 )
814 .unwrap();
815
816 let result = PureRename::apply(&mut file, "foo", "bar");
817 assert_eq!(result.count, 2); let fns = file.functions();
820 assert!(fns.iter().any(|f| f.name == "bar"));
821 assert!(!fns.iter().any(|f| f.name == "foo"));
822 }
823
824 #[test]
825 fn test_rename_struct() {
826 let mut file = PureFile::from_source(
827 r#"
828 struct Point { x: i32, y: i32 }
829 fn main() {
830 let p: Point = Point { x: 0, y: 0 };
831 }
832 "#,
833 )
834 .unwrap();
835
836 let result = PureRename::apply(&mut file, "Point", "Vec2");
837 assert!(result.count >= 2); let structs = file.structs();
840 assert!(structs.iter().any(|s| s.name == "Vec2"));
841 assert!(!structs.iter().any(|s| s.name == "Point"));
842 }
843
844 #[test]
845 fn test_scoped_rename() {
846 let mut file = PureFile::from_source(
847 r#"
848 fn foo() {
849 let x = 1;
850 }
851 fn bar() {
852 let x = 2;
853 }
854 "#,
855 )
856 .unwrap();
857
858 let result = PureRename::rename_local_in_fn(&mut file, "foo", "x", "renamed");
859 assert_eq!(result.count, 1);
860
861 let foo = file
863 .functions()
864 .into_iter()
865 .find(|f| f.name == "foo")
866 .unwrap();
867 assert!(foo.body.stmts.iter().any(|stmt| {
868 if let PureStmt::Local {
869 pattern: PurePattern::Ident { name, .. },
870 ..
871 } = stmt
872 {
873 name == "renamed"
874 } else {
875 false
876 }
877 }));
878
879 let bar = file
881 .functions()
882 .into_iter()
883 .find(|f| f.name == "bar")
884 .unwrap();
885 assert!(bar.body.stmts.iter().any(|stmt| {
886 if let PureStmt::Local {
887 pattern: PurePattern::Ident { name, .. },
888 ..
889 } = stmt
890 {
891 name == "x"
892 } else {
893 false
894 }
895 }));
896 }
897
898 #[test]
899 fn test_cow_rename() {
900 let original = PureFile::from_source("fn foo() {}").unwrap();
901
902 let (renamed, result) = PureRename::apply_cow(&original, "foo", "bar");
903
904 assert_eq!(result.count, 1);
905
906 assert!(original.functions().iter().any(|f| f.name == "foo"));
908
909 assert!(renamed.functions().iter().any(|f| f.name == "bar"));
911 }
912
913 #[test]
914 fn test_parallel_rename() {
915 use std::sync::Arc;
916 use std::thread;
917
918 let file = PureFile::from_source(
919 r#"
920 fn alpha() {}
921 fn beta() {}
922 fn gamma() {}
923 "#,
924 )
925 .unwrap();
926
927 let shared = Arc::new(file);
928
929 let handles: Vec<_> = vec!["alpha", "beta", "gamma"]
931 .into_iter()
932 .map(|old_name| {
933 let f = Arc::clone(&shared);
934 let new_name = format!("{}_renamed", old_name);
935 thread::spawn(move || {
936 let (renamed, result) = PureRename::apply_cow(&f, old_name, &new_name);
937 (renamed.functions().len(), result.count)
938 })
939 })
940 .collect();
941
942 for handle in handles {
943 let (fn_count, rename_count) = handle.join().unwrap();
944 assert_eq!(fn_count, 3);
945 assert_eq!(rename_count, 1);
946 }
947 }
948}