Skip to main content

airl_patch/
impact.rs

1//! Impact analysis: determine which parts of a module are affected by a patch.
2
3use airl_ir::ids::{FuncId, TypeId};
4use airl_ir::module::Module;
5use serde::{Deserialize, Serialize};
6
7use crate::ops::PatchOp;
8use crate::traverse;
9
10/// The impact of a patch on a module: which functions and types are affected.
11#[derive(Clone, Debug, Default, Serialize, Deserialize)]
12pub struct Impact {
13    /// Function IDs whose body, signature, or effects were modified.
14    pub affected_functions: Vec<FuncId>,
15    /// Type IDs whose definition was modified.
16    pub affected_types: Vec<TypeId>,
17}
18
19/// Analyze the impact of a set of patch operations on a module.
20pub fn analyze_impact(module: &Module, ops: &[PatchOp]) -> Impact {
21    let mut affected_functions = Vec::new();
22    let affected_types = Vec::new();
23
24    for op in ops {
25        match op {
26            PatchOp::ReplaceNode { target, .. } => {
27                let funcs = traverse::functions_containing_node(module, target);
28                for f in funcs {
29                    if !affected_functions.contains(&f) {
30                        affected_functions.push(f);
31                    }
32                }
33            }
34            PatchOp::AddFunction { func } => {
35                if !affected_functions.contains(&func.id) {
36                    affected_functions.push(func.id.clone());
37                }
38            }
39            PatchOp::RemoveFunction { func_id } => {
40                if !affected_functions.contains(func_id) {
41                    affected_functions.push(func_id.clone());
42                }
43            }
44            PatchOp::RenameSymbol { scope, .. } => {
45                if let Some(func_id) = scope {
46                    if !affected_functions.contains(func_id) {
47                        affected_functions.push(func_id.clone());
48                    }
49                } else {
50                    // Global rename affects all functions
51                    for func in module.functions() {
52                        if !affected_functions.contains(&func.id) {
53                            affected_functions.push(func.id.clone());
54                        }
55                    }
56                }
57            }
58            PatchOp::AddEffect { func_id, .. } | PatchOp::RemoveEffect { func_id, .. } => {
59                if !affected_functions.contains(func_id) {
60                    affected_functions.push(func_id.clone());
61                }
62            }
63            PatchOp::AddImport { .. } | PatchOp::RemoveImport { .. } => {
64                // Imports don't directly affect functions or types,
65                // but could be tracked as module-level changes
66            }
67        }
68    }
69
70    Impact {
71        affected_functions,
72        affected_types,
73    }
74}