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<_> = func
66 .parameters()
67 .iter()
68 .filter(|p| self.is_void_ptr(p.param_type()))
69 .collect();
70
71 if void_ptr_params.is_empty() {
72 return results;
73 }
74
75 let pattern = self.detect_pattern(func, &void_ptr_params);
77
78 for param in void_ptr_params {
80 let mut info = VoidPtrInfo {
81 param_name: param.name().to_string(),
82 pattern: pattern.clone(),
83 inferred_types: Vec::new(),
84 constraints: Vec::new(),
85 };
86
87 self.analyze_body(func.body(), param.name(), &mut info);
89
90 results.push(info);
91 }
92
93 results
94 }
95
96 fn is_void_ptr(&self, ty: &HirType) -> bool {
97 matches!(ty, HirType::Pointer(inner) if matches!(inner.as_ref(), HirType::Void))
98 }
99
100 fn detect_pattern(
101 &self,
102 func: &HirFunction,
103 void_params: &[&decy_hir::HirParameter],
104 ) -> VoidPtrPattern {
105 let param_count = void_params.len();
106 let has_size_param = func
107 .parameters()
108 .iter()
109 .any(|p| p.name().contains("size") || p.name() == "n" || p.name() == "len");
110 let returns_int = matches!(func.return_type(), HirType::Int);
111
112 if param_count == 2 && has_size_param && func.name() == "swap" {
114 return VoidPtrPattern::Swap;
115 }
116
117 if param_count == 2
119 && returns_int
120 && (func.name().contains("cmp") || func.name() == "compare")
121 {
122 return VoidPtrPattern::Compare;
123 }
124
125 if param_count == 2 && has_size_param {
127 let names: Vec<&str> = void_params.iter().map(|p| p.name()).collect();
128 if names.contains(&"dest") || names.contains(&"src") || func.name().contains("copy") {
129 return VoidPtrPattern::Copy;
130 }
131 }
132
133 VoidPtrPattern::Generic
134 }
135
136 fn analyze_body(&self, stmts: &[HirStatement], param_name: &str, info: &mut VoidPtrInfo) {
137 for stmt in stmts {
138 self.analyze_statement(stmt, param_name, info);
139 }
140 }
141
142 fn analyze_statement(&self, stmt: &HirStatement, param_name: &str, info: &mut VoidPtrInfo) {
143 match stmt {
144 HirStatement::VariableDeclaration {
145 initializer: Some(init),
146 ..
147 } => {
148 self.analyze_expression(init, param_name, info);
149 }
150 HirStatement::DerefAssignment { target, value } => {
151 if self.expr_uses_param(target, param_name)
153 && !info.constraints.contains(&TypeConstraint::Mutable)
154 {
155 info.constraints.push(TypeConstraint::Mutable);
156 }
157 if matches!(value, HirExpression::Dereference(_))
159 && !info.constraints.contains(&TypeConstraint::Clone)
160 {
161 info.constraints.push(TypeConstraint::Clone);
162 }
163 self.analyze_expression(target, param_name, info);
164 self.analyze_expression(value, param_name, info);
165 }
166 HirStatement::If {
167 condition,
168 then_block,
169 else_block,
170 ..
171 } => {
172 self.analyze_expression(condition, param_name, info);
173 self.analyze_body(then_block, param_name, info);
174 if let Some(else_stmts) = else_block {
175 self.analyze_body(else_stmts, param_name, info);
176 }
177 }
178 HirStatement::While {
179 condition, body, ..
180 } => {
181 self.analyze_expression(condition, param_name, info);
182 self.analyze_body(body, param_name, info);
183 }
184 HirStatement::For { body, .. } => {
185 self.analyze_body(body, param_name, info);
186 }
187 HirStatement::Expression(expr) => {
188 self.analyze_expression(expr, param_name, info);
189 }
190 HirStatement::Return(Some(expr)) => {
191 self.analyze_expression(expr, param_name, info);
192 }
193 _ => {}
194 }
195 }
196
197 fn analyze_expression(&self, expr: &HirExpression, param_name: &str, info: &mut VoidPtrInfo) {
198 match expr {
199 HirExpression::Cast {
200 expr: inner,
201 target_type,
202 } => {
203 if self.expr_uses_param(inner, param_name) {
205 if let HirType::Pointer(inner_type) = target_type {
206 if !info.inferred_types.contains(inner_type) {
207 info.inferred_types.push((**inner_type).clone());
208 }
209 }
210 }
211 }
212 HirExpression::BinaryOp { op, left, right } => {
213 let uses_param = self.expr_uses_param(left, param_name)
215 || self.expr_uses_param(right, param_name);
216 if uses_param {
217 use decy_hir::BinaryOperator;
218 match op {
219 BinaryOperator::LessThan
220 | BinaryOperator::GreaterThan
221 | BinaryOperator::LessEqual
222 | BinaryOperator::GreaterEqual => {
223 if !info.constraints.contains(&TypeConstraint::PartialOrd) {
224 info.constraints.push(TypeConstraint::PartialOrd);
225 }
226 }
227 BinaryOperator::Equal | BinaryOperator::NotEqual => {
228 if !info.constraints.contains(&TypeConstraint::PartialEq) {
229 info.constraints.push(TypeConstraint::PartialEq);
230 }
231 }
232 _ => {}
233 }
234 }
235 self.analyze_expression(left, param_name, info);
236 self.analyze_expression(right, param_name, info);
237 }
238 HirExpression::Dereference(inner) => {
239 self.analyze_expression(inner, param_name, info);
240 }
241 HirExpression::FunctionCall { arguments, .. } => {
242 for arg in arguments {
243 self.analyze_expression(arg, param_name, info);
244 }
245 }
246 _ => {}
247 }
248 }
249
250 fn expr_uses_param(&self, expr: &HirExpression, param_name: &str) -> bool {
251 match expr {
252 HirExpression::Variable(name) => name == param_name,
253 HirExpression::Cast { expr: inner, .. } => self.expr_uses_param(inner, param_name),
254 HirExpression::Dereference(inner) => self.expr_uses_param(inner, param_name),
255 _ => false,
256 }
257 }
258}
259
260impl Default for VoidPtrAnalyzer {
261 fn default() -> Self {
262 Self::new()
263 }
264}