1use decy_hir::{HirExpression, HirFunction, HirStatement, HirType};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum VoidPtrPattern {
11 Generic,
13 Swap,
15 Compare,
17 Copy,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum TypeConstraint {
24 Readable,
26 Mutable,
28 Copy,
30 Clone,
32 PartialOrd,
34 PartialEq,
36}
37
38#[derive(Debug, Clone)]
40pub struct VoidPtrInfo {
41 pub param_name: String,
43 pub pattern: VoidPtrPattern,
45 pub inferred_types: Vec<HirType>,
47 pub constraints: Vec<TypeConstraint>,
49}
50
51pub struct VoidPtrAnalyzer;
53
54impl VoidPtrAnalyzer {
55 pub fn new() -> Self {
57 Self
58 }
59
60 pub fn analyze(&self, func: &HirFunction) -> Vec<VoidPtrInfo> {
62 let mut results = Vec::new();
63
64 let void_ptr_params: Vec<_> =
66 func.parameters().iter().filter(|p| self.is_void_ptr(p.param_type())).collect();
67
68 if void_ptr_params.is_empty() {
69 return results;
70 }
71
72 let pattern = self.detect_pattern(func, &void_ptr_params);
74
75 for param in void_ptr_params {
77 let mut info = VoidPtrInfo {
78 param_name: param.name().to_string(),
79 pattern: pattern.clone(),
80 inferred_types: Vec::new(),
81 constraints: Vec::new(),
82 };
83
84 self.analyze_body(func.body(), param.name(), &mut info);
86
87 results.push(info);
88 }
89
90 results
91 }
92
93 fn is_void_ptr(&self, ty: &HirType) -> bool {
94 matches!(ty, HirType::Pointer(inner) if matches!(inner.as_ref(), HirType::Void))
95 }
96
97 fn detect_pattern(
98 &self,
99 func: &HirFunction,
100 void_params: &[&decy_hir::HirParameter],
101 ) -> VoidPtrPattern {
102 let param_count = void_params.len();
103 let has_size_param = func
104 .parameters()
105 .iter()
106 .any(|p| p.name().contains("size") || p.name() == "n" || p.name() == "len");
107 let returns_int = matches!(func.return_type(), HirType::Int);
108
109 if param_count == 2 && has_size_param && func.name() == "swap" {
111 return VoidPtrPattern::Swap;
112 }
113
114 if param_count == 2
116 && returns_int
117 && (func.name().contains("cmp") || func.name() == "compare")
118 {
119 return VoidPtrPattern::Compare;
120 }
121
122 if param_count == 2 && has_size_param {
124 let names: Vec<&str> = void_params.iter().map(|p| p.name()).collect();
125 if names.contains(&"dest") || names.contains(&"src") || func.name().contains("copy") {
126 return VoidPtrPattern::Copy;
127 }
128 }
129
130 VoidPtrPattern::Generic
131 }
132
133 fn analyze_body(&self, stmts: &[HirStatement], param_name: &str, info: &mut VoidPtrInfo) {
134 for stmt in stmts {
135 self.analyze_statement(stmt, param_name, info);
136 }
137 }
138
139 fn analyze_statement(&self, stmt: &HirStatement, param_name: &str, info: &mut VoidPtrInfo) {
140 match stmt {
141 HirStatement::VariableDeclaration { initializer: Some(init), .. } => {
142 self.analyze_expression(init, param_name, info);
143 }
144 HirStatement::DerefAssignment { target, value } => {
145 if self.expr_uses_param(target, param_name)
147 && !info.constraints.contains(&TypeConstraint::Mutable)
148 {
149 info.constraints.push(TypeConstraint::Mutable);
150 }
151 if matches!(value, HirExpression::Dereference(_))
153 && !info.constraints.contains(&TypeConstraint::Clone)
154 {
155 info.constraints.push(TypeConstraint::Clone);
156 }
157 self.analyze_expression(target, param_name, info);
158 self.analyze_expression(value, param_name, info);
159 }
160 HirStatement::If { condition, then_block, else_block, .. } => {
161 self.analyze_expression(condition, param_name, info);
162 self.analyze_body(then_block, param_name, info);
163 if let Some(else_stmts) = else_block {
164 self.analyze_body(else_stmts, param_name, info);
165 }
166 }
167 HirStatement::While { condition, body, .. } => {
168 self.analyze_expression(condition, param_name, info);
169 self.analyze_body(body, param_name, info);
170 }
171 HirStatement::For { body, .. } => {
172 self.analyze_body(body, param_name, info);
173 }
174 HirStatement::Expression(expr) => {
175 self.analyze_expression(expr, param_name, info);
176 }
177 HirStatement::Return(Some(expr)) => {
178 self.analyze_expression(expr, param_name, info);
179 }
180 _ => {}
181 }
182 }
183
184 fn analyze_expression(&self, expr: &HirExpression, param_name: &str, info: &mut VoidPtrInfo) {
185 match expr {
186 HirExpression::Cast { expr: inner, target_type } => {
187 if self.expr_uses_param(inner, param_name) {
189 if let HirType::Pointer(inner_type) = target_type {
190 if !info.inferred_types.contains(inner_type) {
191 info.inferred_types.push((**inner_type).clone());
192 }
193 }
194 }
195 }
196 HirExpression::BinaryOp { op, left, right } => {
197 let uses_param = self.expr_uses_param(left, param_name)
199 || self.expr_uses_param(right, param_name);
200 if uses_param {
201 use decy_hir::BinaryOperator;
202 match op {
203 BinaryOperator::LessThan
204 | BinaryOperator::GreaterThan
205 | BinaryOperator::LessEqual
206 | BinaryOperator::GreaterEqual => {
207 if !info.constraints.contains(&TypeConstraint::PartialOrd) {
208 info.constraints.push(TypeConstraint::PartialOrd);
209 }
210 }
211 BinaryOperator::Equal | BinaryOperator::NotEqual => {
212 if !info.constraints.contains(&TypeConstraint::PartialEq) {
213 info.constraints.push(TypeConstraint::PartialEq);
214 }
215 }
216 _ => {}
217 }
218 }
219 self.analyze_expression(left, param_name, info);
220 self.analyze_expression(right, param_name, info);
221 }
222 HirExpression::Dereference(inner) => {
223 self.analyze_expression(inner, param_name, info);
224 }
225 HirExpression::FunctionCall { arguments, .. } => {
226 for arg in arguments {
227 self.analyze_expression(arg, param_name, info);
228 }
229 }
230 _ => {}
231 }
232 }
233
234 fn expr_uses_param(&self, expr: &HirExpression, param_name: &str) -> bool {
235 match expr {
236 HirExpression::Variable(name) => name == param_name,
237 HirExpression::Cast { expr: inner, .. } => self.expr_uses_param(inner, param_name),
238 HirExpression::Dereference(inner) => self.expr_uses_param(inner, param_name),
239 _ => false,
240 }
241 }
242}
243
244impl Default for VoidPtrAnalyzer {
245 fn default() -> Self {
246 Self::new()
247 }
248}