rem_repairer/
common.rs

1use log::{debug, info};
2use proc_macro2::Span;
3use quote::ToTokens;
4use regex::Regex;
5use rem_utils::format_source;
6use serde::{Deserialize, Serialize};
7use std::borrow::BorrowMut;
8use std::collections::HashMap;
9use std::fs;
10use std::io::{BufWriter, Write};
11use std::process::Command;
12use syn::{
13    visit_mut::VisitMut, ExprCall, ExprMethodCall, FnArg, GenericArgument, GenericParam,
14    ItemFn, Lifetime, PredicateLifetime, ReturnType, Signature,
15    TypeReference, WhereClause, WherePredicate,
16};
17use std::fmt::{
18    self,
19    Debug,
20    Formatter
21};
22
23pub struct RepairerInput {
24    pub input_code: String,
25    pub fn_name: String,
26    pub repair_systems: Vec<Box<dyn RepairSystem>>,
27}
28
29impl Clone for RepairerInput {
30    fn clone(&self) -> Self {
31        Self {
32            input_code: self.input_code.clone(),
33            fn_name: self.fn_name.clone(),
34            repair_systems: self.repair_systems.iter().map(|system| system.clone()).collect(),
35        }
36    }
37}
38
39////////////////////////////////////////////////////////////////////////////////////////////////////
40////////////////////////////////     REPAIR HELPERS     ////////////////////////////////////////////
41////////////////////////////////////////////////////////////////////////////////////////////////////
42pub struct RepairResult {
43    pub success: bool,
44    pub repair_count: i32,
45    #[allow(dead_code)]
46    pub has_non_elidible_lifetime: bool,
47    #[allow(dead_code)]
48    pub has_struct_lt: bool,
49}
50
51pub trait RepairSystem: std::fmt::Debug {
52    fn name(&self) -> &str;
53    fn repair_project(&self, src_path: &str, manifest_path: &str, fn_name: &str) -> RepairResult;
54    fn repair_file(&self, file_name: &str, new_file_name: &str) -> RepairResult;
55    fn repair_function(&self, file_name: &str, new_file_name: &str, fn_name: &str) -> RepairResult;
56    fn clone_box(&self) -> Box<dyn RepairSystem>;
57}
58
59impl Clone for Box<dyn RepairSystem> {
60    fn clone(&self) -> Box<dyn RepairSystem> {
61        self.clone_box()
62    }
63}
64
65// Wrapper for RepairSystem trait objects that implements Debug
66pub struct DebugRepairSystem<'a>(pub &'a dyn RepairSystem);
67
68impl<'a> Debug for DebugRepairSystem<'a> {
69    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
70        f.debug_struct("RepairSystem")
71            .field("name", &self.0.name())
72            .finish()
73    }
74}
75
76#[derive(Serialize, Deserialize, Debug)]
77pub struct RustcError {
78    pub rendered: String,
79    pub spans: Vec<RustcSpan>,
80}
81
82#[derive(Serialize, Deserialize, Debug)]
83pub struct RustcSpan {
84    pub file_name: String,
85}
86
87pub fn repair_standard_help(stderr: &str, new_file_name: &str) -> bool {
88    let deserializer = serde_json::Deserializer::from_str(stderr);
89    let stream = deserializer.into_iter::<RustcError>();
90    let mut helped = false;
91    for item in stream {
92        let rendered = match item {
93            Ok(i) => i.rendered,
94            Err(_) => stderr.to_string(),
95        };
96        let re = Regex::new(r"help: consider.+\n.*\n(?P<line_number>\d+) \| (?P<replacement>.+)\n")
97            .unwrap();
98        let help_lines = re.captures_iter(rendered.as_str());
99
100        let file_content: String = fs::read_to_string(&new_file_name).unwrap().parse().unwrap();
101
102        let lines = file_content.split("\n");
103        let mut lines_modifiable = Vec::new();
104        for (_, line) in lines.enumerate() {
105            lines_modifiable.push(line);
106        }
107
108        let mut current_line = 0;
109
110        let out_file = fs::File::create(&new_file_name).unwrap();
111        let mut writer = BufWriter::new(out_file);
112        for captured in help_lines {
113            /*
114            println!(
115                "line: {:?}, fn: {:?} {}",
116                &captured["line_number"], &captured["replacement"], current_line,
117            );
118             */
119
120            let line_number = match captured["line_number"].parse::<usize>() {
121                Ok(n) => n,
122                Err(_) => continue,
123            };
124            let replacement = &captured["replacement"];
125            if replacement.contains("&'lifetime") {
126                continue;
127            }
128
129            helped = true;
130            while current_line < line_number - 1 {
131                writeln!(writer, "{}", lines_modifiable[current_line]).unwrap();
132                current_line += 1;
133            }
134            writeln!(writer, "{}", replacement).unwrap();
135            current_line += 1;
136        }
137        while current_line < lines_modifiable.len() {
138            writeln!(writer, "{}", lines_modifiable[current_line]).unwrap();
139            current_line += 1;
140        }
141    }
142    helped
143}
144
145struct FnLifetimeBounder<'a> {
146    fn_name: &'a str,
147    lifetime: &'a str,
148    bound: &'a str,
149    success: bool,
150}
151
152impl VisitMut for FnLifetimeBounder<'_> {
153    fn visit_impl_item_fn_mut(&mut self, i: &mut syn::ImplItemFn) {
154        let id = i.sig.ident.to_string();
155        //println!("caller name: {}, at: {}", self.caller_fn_name, &id);
156        match id == self.fn_name.to_string() {
157            false => (),
158            true => self.fn_lifetime_bounder(&mut i.sig),
159        }
160        syn::visit_mut::visit_impl_item_fn_mut(self, i);
161    }
162
163    fn visit_item_fn_mut(&mut self, i: &mut ItemFn) {
164        let id = i.sig.ident.to_string();
165        match id == self.fn_name.to_string() {
166            false => (),
167            true => self.fn_lifetime_bounder(&mut i.sig),
168        }
169    }
170
171    fn visit_trait_item_fn_mut(&mut self, i: &mut syn::TraitItemFn) {
172        let id = i.sig.ident.to_string();
173        //println!("caller name: {}, at: {}", self.caller_fn_name, &id);
174        match id == self.fn_name.to_string() {
175            false => (),
176            true => self.fn_lifetime_bounder(&mut i.sig),
177        }
178    syn::visit_mut::visit_trait_item_fn_mut(self, i);
179    }
180}
181
182impl FnLifetimeBounder<'_> {
183    fn fn_lifetime_bounder(&mut self, sig: &mut Signature) {
184        let gen = &mut sig.generics;
185        let wc = gen.where_clause.get_or_insert(WhereClause {
186            where_token: Default::default(),
187            predicates: Default::default(),
188        });
189        let mut wp = PredicateLifetime {
190            lifetime: Lifetime::new(self.lifetime, Span::call_site()),
191            colon_token: Default::default(),
192            bounds: Default::default(),
193        };
194        wp.bounds.push(Lifetime::new(self.bound, Span::call_site()));
195        wc.predicates.push(WherePredicate::Lifetime(wp));
196        self.success = true
197    }
198}
199
200pub fn repair_bounds_help(stderr: &str, new_file_name: &str, fn_name: &str) -> bool {
201    let deserializer = serde_json::Deserializer::from_str(stderr);
202    let stream = deserializer.into_iter::<RustcError>();
203    let mut helped = false;
204    for item in stream {
205        let rendered = match item {
206            Ok(i) => i.rendered,
207            Err(_) => stderr.to_string(),
208        };
209        let re = Regex::new(r"= help: consider.+bound: `(?P<constraint_lhs>'[a-z0-9]+): (?P<constraint_rhs>'[a-z0-9]+)`").unwrap();
210        let help_lines = re.captures_iter(rendered.as_str());
211        /*
212            &caps["line_number"],
213            &caps["fn_sig"],
214            &caps["constraint_lhs"],
215            &caps["constraint_rhs"],
216        */
217        for captured in help_lines {
218            // println!("found helps: {}, {}",
219            //          &captured["constraint_lhs"],
220            //          &captured["constraint_rhs"]);
221            let file_content: String = fs::read_to_string(&new_file_name).unwrap().parse().unwrap();
222            let mut file = syn::parse_str::<syn::File>(file_content.as_str())
223                .map_err(|e| format!("{:?}", e))
224                .unwrap();
225            let mut visit = FnLifetimeBounder {
226                fn_name,
227                lifetime: &captured["constraint_lhs"],
228                bound: &captured["constraint_rhs"],
229                success: false,
230            };
231            visit.visit_file_mut(&mut file);
232            let file = file.into_token_stream().to_string();
233            match visit.success {
234                true => {
235                    fs::write(new_file_name.to_string(), format_source(&file)).unwrap();
236                    helped = true;
237                }
238                false => (),
239            }
240        }
241    }
242    helped
243}
244
245pub fn repair_iteration(
246    compile_cmd: &mut Command,
247    process_errors: &dyn Fn(&str) -> bool,
248    print_stats: bool,
249    max_iterations: Option<i32>,
250) -> RepairResult {
251    let mut count = 0;
252    let max_iterations = max_iterations.unwrap_or(25);
253    let mut repair_result = RepairResult {
254        success: false,
255        repair_count: 0,
256        has_non_elidible_lifetime: false,
257        has_struct_lt: false,
258    };
259
260    let success = loop {
261        let out = compile_cmd.output().unwrap();
262        let stderr = String::from_utf8_lossy(&out.stderr);
263        if out.status.success() {
264            break true;
265        }
266        count += 1;
267
268        let temp = stderr.to_string();
269        if !process_errors(temp.as_str()) {
270            break false;
271        }
272        if max_iterations == count {
273            break false;
274        }
275    };
276
277    if print_stats {
278        info!("repair count: {}", count);
279        info!("status: {}", success);
280    }
281
282    repair_result.success = success;
283    repair_result.repair_count = count;
284    repair_result
285}
286
287////////////////////////////////////////////////////////////////////////////////////////////////////
288////////////////////////////////    ELIDING LIFETIMES   ////////////////////////////////////////////
289////////////////////////////////////////////////////////////////////////////////////////////////////
290struct FnLifetimeEliderTypeHelper<'a> {
291    cannot_elide: &'a Vec<String>,
292    lt_count: &'a HashMap<&'a String, i32>,
293}
294
295impl VisitMut for FnLifetimeEliderTypeHelper<'_> {
296    // don't elide fully just replace with '_
297    // fn visit_angle_bracketed_generic_arguments_mut(
298    //     &mut self,
299    //     i: &mut AngleBracketedGenericArguments,
300    // ) {
301    //     i.args = i
302    //         .args
303    //         .clone()
304    //         .into_iter()
305    //         .filter(|arg| match arg {
306    //             GenericArgument::Lifetime(lt) => {
307    //                 let id = lt.to_string();
308    //                 if !self.lt_count.contains_key(&id) {
309    //                     false
310    //                 } else {
311    //                     let result =
312    //                         self.cannot_elide.contains(&id) || *self.lt_count.get(&id).unwrap() > 1;
313    //                     result
314    //                 }
315    //             }
316    //             _ => true,
317    //         })
318    //         .collect();
319    //     syn::visit_mut::visit_angle_bracketed_generic_arguments_mut(self, i);
320    // }
321
322    fn visit_type_reference_mut(&mut self, i: &mut TypeReference) {
323        match &mut i.lifetime {
324            None => (),
325            Some(lt) => {
326                let id = lt.to_string();
327                if !self.cannot_elide.contains(&id)
328                    && (!self.lt_count.contains_key(&id) || *self.lt_count.get(&id).unwrap() <= 1)
329                {
330                    i.lifetime = None
331                }
332            }
333        };
334        syn::visit_mut::visit_type_reference_mut(self, i);
335    }
336}
337
338struct FnLifetimeEliderArgHelper<'a> {
339    cannot_elide: &'a Vec<String>,
340    lt_count: &'a HashMap<&'a String, i32>,
341}
342
343impl VisitMut for FnLifetimeEliderArgHelper<'_> {
344    fn visit_fn_arg_mut(&mut self, i: &mut FnArg) {
345        match i {
346            FnArg::Typed(t) => {
347                let mut type_helper = FnLifetimeEliderTypeHelper {
348                    cannot_elide: self.cannot_elide,
349                    lt_count: self.lt_count,
350                };
351                type_helper.visit_type_mut(t.ty.as_mut());
352            }
353            FnArg::Receiver(_) => (), // cannot elide if there is self
354        }
355    }
356}
357
358struct FnLifetimeElider<'a> {
359    fn_name: &'a str,
360    annotations_left: bool,
361    has_struct_lt: bool,
362}
363
364struct LtGetterElider<'a> {
365    v: &'a mut Vec<String>,
366}
367impl VisitMut for LtGetterElider<'_> {
368    fn visit_lifetime_mut(&mut self, i: &mut Lifetime) {
369        let id = i.to_string();
370        self.v.push(id.to_string());
371        syn::visit_mut::visit_lifetime_mut(self, i)
372    }
373}
374
375struct ChangeLtHelperElider<'a> {
376    map: &'a HashMap<String, String>,
377    has_struct_lt: bool,
378}
379
380impl VisitMut for ChangeLtHelperElider<'_> {
381    fn visit_generic_argument_mut(&mut self, i: &mut GenericArgument) {
382        debug!("generic: {:?}", i);
383        match i {
384            GenericArgument::Lifetime(l) => {
385                let id = l.to_string();
386                debug!("generic lt: {:?}", &id);
387                self.has_struct_lt = true;
388                match self.map.get(&id) {
389                    Some(new_lt) => *l = Lifetime::new(new_lt.as_str(), Span::call_site()),
390                    None => *l = Lifetime::new("'_", Span::call_site()),
391                }
392            }
393            _ => syn::visit_mut::visit_generic_argument_mut(self, i),
394        }
395    }
396    fn visit_lifetime_mut(&mut self, i: &mut Lifetime) {
397        let id = i.to_string();
398        match self.map.get(&id) {
399            Some(new_lt) => *i = Lifetime::new(new_lt.as_str(), Span::call_site()),
400            None => (),
401        }
402        syn::visit_mut::visit_lifetime_mut(self, i)
403    }
404}
405
406impl VisitMut for FnLifetimeElider<'_> {
407    fn visit_impl_item_fn_mut(&mut self, i: &mut syn::ImplItemFn) {
408        let id = i.sig.ident.to_string();
409        //println!("caller name: {}, at: {}", self.caller_fn_name, &id);
410        match id == self.fn_name.to_string() {
411            false => (),
412            true => self.fn_lifetime_elider(&mut i.sig),
413        }
414        syn::visit_mut::visit_impl_item_fn_mut(self, i);
415    }
416
417    fn visit_item_fn_mut(&mut self, i: &mut ItemFn) {
418        let id = i.sig.ident.to_string();
419        match id == self.fn_name.to_string() {
420            false => (),
421            true => self.fn_lifetime_elider(&mut i.sig),
422        }
423    }
424
425    fn visit_trait_item_fn_mut(&mut self, i: &mut syn::TraitItemFn) {
426        let id = i.sig.ident.to_string();
427        //println!("caller name: {}, at: {}", self.caller_fn_name, &id);
428        match id == self.fn_name.to_string() {
429            false => (),
430            true => self.fn_lifetime_elider(&mut i.sig),
431        }
432        syn::visit_mut::visit_trait_item_fn_mut(self, i);
433    }
434}
435
436impl FnLifetimeElider<'_> {
437    fn fn_lifetime_elider(&mut self, sig: &mut Signature) {
438        // println!("original : {}", i.sig.clone().into_token_stream().to_string());
439        let gen = &mut sig.generics;
440        let mut cannot_elide = vec![];
441        match &gen.where_clause {
442            None => (),
443            Some(wc) => wc.predicates.iter().for_each(|wp| match wp {
444                WherePredicate::Lifetime(lt) => {
445                    cannot_elide.push(lt.lifetime.to_string());
446                    cannot_elide.push(lt.bounds.first().unwrap().to_string())
447                }
448                _ => (),
449            }),
450        }
451        match sig.output.borrow_mut() {
452            ReturnType::Default => (),
453            ReturnType::Type(_, ty) => {
454                let mut get_lt = LtGetterElider {
455                    v: &mut cannot_elide,
456                };
457                get_lt.visit_type_mut(ty.clone().as_mut());
458            }
459        };
460
461        let inputs = &mut sig.inputs;
462        let mut has_receiver = false;
463        let mut map = HashMap::new();
464        let mut v = vec![];
465        inputs.iter_mut().for_each(|fn_arg| {
466            match fn_arg {
467                FnArg::Receiver(_) => has_receiver = true,
468                FnArg::Typed(_) => {
469                    let mut get_lt = LtGetterElider { v: &mut v };
470                    get_lt.visit_fn_arg_mut(fn_arg)
471                }
472            };
473        });
474        match has_receiver {
475            true => (),
476            false => {
477                match sig.output.borrow_mut() {
478                    ReturnType::Default => (),
479                    ReturnType::Type(_, ty) => {
480                        let mut get_lt = LtGetterElider { v: &mut v };
481                        get_lt.visit_type_mut(ty.clone().as_mut());
482                    }
483                };
484                gen.params.iter_mut().for_each(|gp| match gp {
485                    GenericParam::Lifetime(_) => (),
486                    gp => {
487                        let mut get_lt = LtGetterElider { v: &mut v };
488                        get_lt.visit_generic_param_mut(gp);
489                    }
490                });
491                v.iter().for_each(|lt| {
492                    match map.contains_key(lt) {
493                        true => map.insert(lt, *map.get(lt).unwrap() + 1),
494                        false => map.insert(lt, 1),
495                    };
496                });
497                let mut fn_arg_helper = FnLifetimeEliderArgHelper {
498                    cannot_elide: &cannot_elide,
499                    lt_count: &map,
500                };
501                inputs
502                    .iter_mut()
503                    .for_each(|fn_arg| fn_arg_helper.visit_fn_arg_mut(fn_arg));
504
505                match sig.output.borrow_mut() {
506                    ReturnType::Default => (),
507                    ReturnType::Type(_, ty) => {
508                        let mut type_helper = FnLifetimeEliderTypeHelper {
509                            cannot_elide: &cannot_elide,
510                            lt_count: &map,
511                        };
512                        type_helper.visit_type_mut(ty.as_mut());
513                    }
514                };
515                gen.params.iter_mut().for_each(|gp| match gp {
516                    GenericParam::Lifetime(_) => (),
517                    gp => {
518                        let mut type_helper = FnLifetimeEliderTypeHelper {
519                            cannot_elide: &cannot_elide,
520                            lt_count: &map,
521                        };
522                        type_helper.visit_generic_param_mut(gp);
523                    }
524                });
525                gen.params = gen
526                    .params
527                    .iter()
528                    .cloned()
529                    .filter(|g| match g {
530                        GenericParam::Lifetime(lt) => {
531                            let id = lt.lifetime.to_string();
532                            if !map.contains_key(&id) {
533                                false
534                            } else {
535                                let result =
536                                    *map.get(&id).unwrap() > 1 || cannot_elide.contains(&id);
537                                debug!("lt: {}, result: {}", id, result);
538                                result
539                            }
540                        }
541                        _ => true,
542                    })
543                    .collect();
544
545                let mut lt_count = 0;
546                let mut new_lts = HashMap::new();
547                gen.params.iter_mut().for_each(|gp| match gp {
548                    GenericParam::Lifetime(lt) => {
549                        let id = lt.lifetime.to_string();
550                        self.annotations_left = true;
551                        new_lts.insert(id, format!("'lt{}", lt_count));
552                        lt.lifetime =
553                            Lifetime::new(format!("'lt{}", lt_count).as_str(), Span::call_site());
554                        lt_count += 1
555                    }
556                    _ => (),
557                });
558                gen.params.iter_mut().for_each(|gp| match gp {
559                    GenericParam::Lifetime(_) => (),
560                    gp => {
561                        let mut change_lt = ChangeLtHelperElider {
562                            map: &new_lts,
563                            has_struct_lt: false,
564                        };
565                        if change_lt.has_struct_lt {
566                            self.has_struct_lt = true;
567                        }
568                        change_lt.visit_generic_param_mut(gp);
569                    }
570                });
571                match &mut gen.where_clause {
572                    None => (),
573                    Some(wc) => wc.predicates.iter_mut().for_each(|wp| match wp {
574                        WherePredicate::Lifetime(lt) => {
575                            let id = lt.lifetime.to_string();
576                            match new_lts.get(&id) {
577                                Some(new_lt) => {
578                                    lt.lifetime = Lifetime::new(new_lt.as_str(), Span::call_site())
579                                }
580                                None => (),
581                            };
582                            lt.bounds.iter_mut().for_each(|bound| {
583                                let id = bound.to_string();
584                                match new_lts.get(&id) {
585                                    Some(new_lt) => {
586                                        *bound = Lifetime::new(new_lt.as_str(), Span::call_site())
587                                    }
588                                    None => (),
589                                }
590                            })
591                        }
592                        _ => (),
593                    }),
594                }
595                inputs.iter_mut().for_each(|fn_arg| match fn_arg {
596                    FnArg::Receiver(_) => (),
597                    FnArg::Typed(t) => {
598                        let mut change_lt = ChangeLtHelperElider {
599                            map: &new_lts,
600                            has_struct_lt: false,
601                        };
602                        if change_lt.has_struct_lt {
603                            self.has_struct_lt = true;
604                        }
605                        debug!("debugging input: {:?}", t);
606                        change_lt.visit_pat_type_mut(t);
607                    }
608                });
609                match sig.output.borrow_mut() {
610                    ReturnType::Default => (),
611                    ReturnType::Type(_, ty) => {
612                        let mut change_lt = ChangeLtHelperElider {
613                            map: &new_lts,
614                            has_struct_lt: false,
615                        };
616                        if change_lt.has_struct_lt {
617                            self.has_struct_lt = true;
618                        }
619                        change_lt.visit_type_mut(ty.as_mut());
620                    }
621                }
622            }
623        }
624    }
625}
626
627pub struct ElideLifetimeResult {
628    #[allow(dead_code)]
629    pub success: bool,
630    pub annotations_left: bool,
631    pub has_struct_lt: bool,
632}
633
634/**
635Elide lifetimes that are only used once in the inputs and not used in output(s)/bound(s)
636
637Do not elide lifetimes when receiver (self) is in the input
638
639Elision rules are here: https://doc.rust-lang.org/nomicon/lifetime-elision.htm
640*/
641pub fn elide_lifetimes_annotations(new_file_name: &str, fn_name: &str) -> ElideLifetimeResult {
642    let file_content: String = fs::read_to_string(&new_file_name).unwrap().parse().unwrap();
643    let mut file = syn::parse_str::<syn::File>(file_content.as_str())
644        .map_err(|e| format!("{:?}", e))
645        .unwrap();
646    let mut visit = FnLifetimeElider {
647        fn_name,
648        annotations_left: false,
649        has_struct_lt: false,
650    };
651    visit.visit_file_mut(&mut file);
652    let file = file.into_token_stream().to_string();
653    fs::write(new_file_name.to_string(), format_source(&file)).unwrap();
654    ElideLifetimeResult {
655        success: true,
656        annotations_left: visit.annotations_left,
657        has_struct_lt: visit.has_struct_lt,
658    }
659}
660
661////////////////////////////////////////////////////////////////////////////////////////////////////
662////////////////////////////////     CALLEE RENAMER    ////////////////////////////////////////////
663////////////////////////////////////////////////////////////////////////////////////////////////////
664pub struct RenameFn<'a> {
665    pub(crate) callee_name: &'a str,
666    pub(crate) callee_postfix: &'a str,
667}
668
669impl VisitMut for RenameFn<'_> {
670    fn visit_expr_method_call_mut(&mut self, i: &mut ExprMethodCall) {
671        let callee = i.clone().method.into_token_stream().to_string();
672        match callee.contains(self.callee_name) {
673            true => {
674                i.method = syn::parse_str(callee.replace(self.callee_postfix, "").as_str()).unwrap()
675            }
676            false => syn::visit_mut::visit_expr_method_call_mut(self, i),
677        }
678    }
679
680    fn visit_expr_call_mut(&mut self, i: &mut ExprCall) {
681        let callee = i.func.as_ref().into_token_stream().to_string();
682        match callee.contains(self.callee_name) {
683            true => {
684                debug!("callee: {} matched", &callee);
685                *i.func.as_mut() =
686                    syn::parse_str(callee.replace(self.callee_postfix, "").as_str()).unwrap();
687            }
688            false => {}
689        }
690        syn::visit_mut::visit_expr_call_mut(self, i);
691    }
692    fn visit_impl_item_fn_mut(&mut self, i: &mut syn::ImplItemFn) {
693        let callee = i.sig.ident.to_string();
694        match callee.contains(self.callee_name) {
695            true => {
696                i.sig.ident =
697                    syn::parse_str(callee.replace(self.callee_postfix, "").as_str()).unwrap();
698            }
699            false => {}
700        }
701        syn::visit_mut::visit_impl_item_fn_mut(self, i);
702    }
703
704    fn visit_item_fn_mut(&mut self, i: &mut ItemFn) {
705        let callee = i.sig.ident.to_string();
706        match callee.contains(self.callee_name) {
707            true => {
708                i.sig.ident =
709                    syn::parse_str(callee.replace(self.callee_postfix, "").as_str()).unwrap();
710            }
711            false => {}
712        }
713        syn::visit_mut::visit_item_fn_mut(self, i);
714    }
715    fn visit_trait_item_fn_mut(&mut self, i: &mut syn::TraitItemFn) {
716        let callee = i.sig.ident.to_string();
717        match callee.contains(self.callee_name) {
718            true => {
719                i.sig.ident =
720                    syn::parse_str(callee.replace(self.callee_postfix, "").as_str()).unwrap();
721            }
722            false => {}
723        }
724        syn::visit_mut::visit_trait_item_fn_mut(self, i);
725    }
726}
727
728pub fn callee_renamer(new_file_name: &str, fn_name: &str) {
729    let file_content: String = fs::read_to_string(&new_file_name).unwrap().parse().unwrap();
730    let mut file = syn::parse_str::<syn::File>(file_content.as_str())
731        .map_err(|e| format!("{:?}", e))
732        .unwrap();
733    let mut visitor = RenameFn {
734        callee_name: fn_name,
735        callee_postfix: "____EXTRACT_THIS",
736    };
737    visitor.visit_file_mut(&mut file);
738    let file = file.into_token_stream().to_string();
739    fs::write(new_file_name.to_string(), format_source(&file)).unwrap()
740}
741
742////////////////////////////////////////////////////////////////////////////////////////////////////
743////////////////////////////////     PROJECT HELPERS    ////////////////////////////////////////////
744////////////////////////////////////////////////////////////////////////////////////////////////////
745#[derive(Serialize, Deserialize, Debug)]
746pub struct CargoError {
747    pub message: Option<RustcError>,
748}
749
750pub fn repair_iteration_project(
751    compile_cmd: &mut Command,
752    src_path: &str,
753    process_errors: &dyn Fn(&RustcError) -> bool,
754    print_stats: bool,
755    max_iterations: Option<i32>,
756) -> RepairResult {
757    let mut count = 0;
758    let max_iterations = max_iterations.unwrap_or(25);
759    let mut repair_result = RepairResult {
760        success: false,
761        repair_count: 0,
762        has_non_elidible_lifetime: false,
763        has_struct_lt: false,
764    };
765    let success = loop {
766        let out = compile_cmd.output().unwrap();
767        if out.status.success() {
768            info!("repair succeeded");
769            break true;
770        }
771        // cargo give rustc error to stdout not stderr
772        let stdout = String::from_utf8_lossy(&out.stdout);
773        let binding = stdout.to_string();
774        let deserializer = serde_json::Deserializer::from_str(binding.as_str());
775        let stream = deserializer.into_iter::<CargoError>();
776        count += 1;
777
778        let mut help = false;
779        let mut last_failure = format!("");
780        for item in stream {
781            match &item {
782                Ok(item) => match &item.message {
783                    None => {}
784                    Some(message) => {
785                        let spans = &message.spans;
786                        debug!("message: {:?}", &message);
787                        for span in spans {
788                            if src_path.contains(&span.file_name) {
789                                debug!("processing error: {}", &message.rendered);
790                                last_failure = message.rendered.clone();
791                                if process_errors(&message) {
792                                    help = true;
793                                    break;
794                                }
795                            }
796                        }
797                    }
798                },
799                Err(e) => {
800                    debug!("error parsing cargo error:\n{}", e);
801                }
802            }
803        }
804
805        if !help {
806            debug!("last failure:\n{}", last_failure);
807            break false;
808        }
809
810        if max_iterations == count {
811            debug!("last failure:\n{}", last_failure);
812            break false;
813        }
814    };
815
816    if print_stats {
817        info!("repair count: {}", count);
818        info!("status: {}", success);
819    }
820
821    repair_result.success = success;
822    repair_result.repair_count = count;
823    repair_result
824}