1use aiproof_config::Config;
3use aiproof_core::rule::Ctx;
4use std::path::PathBuf;
5
6pub struct FixOutcome {
7 pub fixes_applied: usize,
8 pub files_changed: usize,
9}
10
11pub fn run_fix(
12 paths: &[PathBuf],
13 config: &Config,
14 unsafe_fixes: bool,
15) -> anyhow::Result<FixOutcome> {
16 let files = crate::discovery::discover(paths, config)?;
17 let rules = crate::run::filtered_rules(config);
18 let ctx = Ctx {
19 target_models: config.target_models.as_slice(),
20 max_tokens_budget: config.max_tokens_budget,
21 };
22
23 let mut fixes_applied = 0;
24 let mut files_changed = 0;
25
26 for f in files {
27 if std::fs::metadata(&f.path).is_ok_and(|m| m.len() > 10 * 1024 * 1024) {
28 continue;
29 }
30 let Ok(mut source) = std::fs::read_to_string(&f.path) else {
31 continue;
32 };
33 let original = source.clone();
34
35 for _ in 0..10 {
37 let docs = match aiproof_parse::parse_file(&f.path, &source) {
38 Ok(d) => d,
39 Err(_) => break,
40 };
41 let mut edits_for_pass = Vec::new();
42 for doc in &docs {
43 for rule in &rules {
44 for diag in rule.check(doc, &ctx) {
45 if let Some(fix) = rule.autofix(&diag, doc) {
46 if !unsafe_fixes && !fix.safe {
47 continue;
48 }
49 edits_for_pass.extend(fix.edits);
50 }
51 }
52 }
53 }
54 if edits_for_pass.is_empty() {
55 break;
56 }
57
58 edits_for_pass.sort_by_key(|e| std::cmp::Reverse(e.span.byte_range.start));
62 let mut next_safe_end = source.len();
63 for e in &edits_for_pass {
64 let range = e.span.byte_range.clone();
65 if range.end > source.len() || range.start > range.end {
66 continue;
67 }
68 if range.end > next_safe_end {
70 continue;
71 }
72 if !source.is_char_boundary(range.start) || !source.is_char_boundary(range.end) {
74 continue;
75 }
76 source.replace_range(range.clone(), &e.replacement);
77 next_safe_end = range.start;
78 fixes_applied += 1;
79 }
80 }
81
82 if source != original {
83 let tmp = f.path.with_extension("aiproof-tmp");
85 std::fs::write(&tmp, &source)?;
86 std::fs::rename(&tmp, &f.path)?;
87 files_changed += 1;
88 }
89 }
90
91 Ok(FixOutcome {
92 fixes_applied,
93 files_changed,
94 })
95}