decy_analyzer/output_params.rs
1//! Output parameter detection for C-to-Rust transformation.
2//!
3//! This module identifies C output parameters (pointer parameters written before being read)
4//! and classifies them for transformation to idiomatic Rust return values.
5//!
6//! # Examples
7//!
8//! C code with output parameter:
9//! ```c
10//! int parse(const char* input, int* result) {
11//! *result = 42;
12//! return 0; // 0 = success
13//! }
14//! ```
15//!
16//! This would be transformed to idiomatic Rust:
17//! ```rust,no_run
18//! fn parse(input: &str) -> Result<i32, std::io::Error> {
19//! Ok(42)
20//! }
21//! ```
22
23use decy_hir::HirFunction;
24use std::collections::HashMap;
25
26/// Represents a detected output parameter.
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct OutputParameter {
29 /// Parameter name
30 pub name: String,
31 /// Parameter kind (output vs input-output)
32 pub kind: ParameterKind,
33 /// Whether the function is fallible (returns error codes)
34 pub is_fallible: bool,
35}
36
37/// Classification of parameter usage patterns.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum ParameterKind {
40 /// Pure output parameter (written before read)
41 Output,
42 /// Input-output parameter (read before or during write)
43 InputOutput,
44}
45
46/// Detector for output parameters in C functions.
47#[derive(Debug, Clone)]
48pub struct OutputParamDetector;
49
50impl OutputParamDetector {
51 /// Create a new output parameter detector.
52 pub fn new() -> Self {
53 Self
54 }
55
56 /// Detect output parameters in a function.
57 ///
58 /// # Arguments
59 ///
60 /// * `func` - The HIR function to analyze
61 ///
62 /// # Returns
63 ///
64 /// A vector of detected output parameters.
65 ///
66 /// # Examples
67 ///
68 /// ```
69 /// use decy_analyzer::output_params::OutputParamDetector;
70 /// use decy_hir::HirFunction;
71 ///
72 /// let detector = OutputParamDetector::new();
73 /// // let func = ...; // Create HIR function
74 /// // let params = detector.detect(&func);
75 /// ```
76 pub fn detect(&self, func: &HirFunction) -> Vec<OutputParameter> {
77 let mut results = Vec::new();
78
79 // Track reads and writes for each parameter
80 let mut reads: HashMap<String, bool> = HashMap::new();
81 let mut writes: HashMap<String, bool> = HashMap::new();
82
83 // Initialize tracking for pointer parameters only
84 for param in func.parameters() {
85 if Self::is_pointer_type(param.param_type()) {
86 reads.insert(param.name().to_string(), false);
87 writes.insert(param.name().to_string(), false);
88 }
89 }
90
91 // Analyze function body
92 for stmt in func.body() {
93 Self::analyze_statement_internal(stmt, &mut reads, &mut writes);
94 }
95
96 // Detect fallible functions (multiple return values, typically 0 for success, non-zero for error)
97 let is_fallible = self.is_fallible_function(func);
98
99 // Classify parameters
100 for param in func.parameters() {
101 let param_name = param.name();
102
103 if !Self::is_pointer_type(param.param_type()) {
104 continue;
105 }
106
107 let was_read = reads.get(param_name).copied().unwrap_or(false);
108 let was_written = writes.get(param_name).copied().unwrap_or(false);
109
110 // Output parameter: written but not read (or written before read)
111 if was_written && !was_read {
112 results.push(OutputParameter {
113 name: param_name.to_string(),
114 kind: ParameterKind::Output,
115 is_fallible,
116 });
117 }
118 }
119
120 results
121 }
122
123 /// Check if a type is a pointer type.
124 fn is_pointer_type(ty: &decy_hir::HirType) -> bool {
125 matches!(ty, decy_hir::HirType::Pointer(_))
126 }
127
128 /// Check if a function is fallible (returns error codes).
129 ///
130 /// Heuristics:
131 /// - Return type is Int (common for error codes: 0 = success, -1/1 = error)
132 /// - Void functions are never fallible
133 fn is_fallible_function(&self, func: &HirFunction) -> bool {
134 use decy_hir::HirType;
135
136 // Void functions are not fallible
137 if matches!(func.return_type(), HirType::Void) {
138 return false;
139 }
140
141 // Int return type with output parameters is a strong signal for error codes
142 // Common C pattern: int func(input, output*) where int is 0=success, -1=error
143 matches!(func.return_type(), HirType::Int)
144 }
145
146 /// Analyze a statement to track parameter reads and writes.
147 fn analyze_statement_internal(
148 stmt: &decy_hir::HirStatement,
149 reads: &mut HashMap<String, bool>,
150 writes: &mut HashMap<String, bool>,
151 ) {
152 use decy_hir::{HirExpression, HirStatement};
153
154 match stmt {
155 // Track dereference assignments: *ptr = value
156 HirStatement::DerefAssignment { target, value } => {
157 // Check if target is a parameter (write)
158 if let HirExpression::Variable(var_name) = target {
159 if writes.contains_key(var_name) {
160 // Mark as written only if not already read
161 if !reads.get(var_name).copied().unwrap_or(false) {
162 writes.insert(var_name.clone(), true);
163 }
164 }
165 }
166
167 // Check value expression for reads
168 Self::analyze_expression_internal(value, reads);
169 }
170
171 // Variable declarations can read from parameters
172 HirStatement::VariableDeclaration {
173 initializer: Some(expr),
174 ..
175 } => {
176 Self::analyze_expression_internal(expr, reads);
177 }
178
179 // Assignment can read from parameters
180 HirStatement::Assignment { value, .. } => {
181 Self::analyze_expression_internal(value, reads);
182 }
183
184 // Return statement can read from parameters
185 HirStatement::Return(Some(expr)) => {
186 Self::analyze_expression_internal(expr, reads);
187 }
188
189 // Control flow statements
190 HirStatement::If {
191 condition,
192 then_block,
193 else_block,
194 } => {
195 Self::analyze_expression_internal(condition, reads);
196 for s in then_block {
197 Self::analyze_statement_internal(s, reads, writes);
198 }
199 if let Some(else_stmts) = else_block {
200 for s in else_stmts {
201 Self::analyze_statement_internal(s, reads, writes);
202 }
203 }
204 }
205
206 HirStatement::While { condition, body } => {
207 Self::analyze_expression_internal(condition, reads);
208 for s in body {
209 Self::analyze_statement_internal(s, reads, writes);
210 }
211 }
212
213 HirStatement::For {
214 init,
215 condition,
216 increment,
217 body,
218 } => {
219 // DECY-224: Handle multiple init statements
220 for init_stmt in init {
221 Self::analyze_statement_internal(init_stmt, reads, writes);
222 }
223 if let Some(cond) = condition {
224 Self::analyze_expression_internal(cond, reads);
225 }
226 // DECY-224: Handle multiple increment statements
227 for inc_stmt in increment {
228 Self::analyze_statement_internal(inc_stmt, reads, writes);
229 }
230 for s in body {
231 Self::analyze_statement_internal(s, reads, writes);
232 }
233 }
234
235 HirStatement::Switch {
236 condition,
237 cases,
238 default_case,
239 } => {
240 Self::analyze_expression_internal(condition, reads);
241 for case in cases {
242 for s in &case.body {
243 Self::analyze_statement_internal(s, reads, writes);
244 }
245 }
246 if let Some(default_stmts) = default_case {
247 for s in default_stmts {
248 Self::analyze_statement_internal(s, reads, writes);
249 }
250 }
251 }
252
253 HirStatement::Expression(expr) => {
254 Self::analyze_expression_internal(expr, reads);
255 }
256
257 _ => {}
258 }
259 }
260
261 /// Analyze an expression to track parameter reads.
262 fn analyze_expression_internal(
263 expr: &decy_hir::HirExpression,
264 reads: &mut HashMap<String, bool>,
265 ) {
266 use decy_hir::HirExpression;
267
268 match expr {
269 // Dereferencing a parameter is a read
270 HirExpression::Dereference(inner) => {
271 if let HirExpression::Variable(var_name) = inner.as_ref() {
272 if reads.contains_key(var_name) {
273 reads.insert(var_name.clone(), true);
274 }
275 }
276 }
277
278 // Binary operations
279 HirExpression::BinaryOp { left, right, .. } => {
280 Self::analyze_expression_internal(left, reads);
281 Self::analyze_expression_internal(right, reads);
282 }
283
284 // Unary operations
285 HirExpression::UnaryOp { operand, .. } => {
286 Self::analyze_expression_internal(operand, reads);
287 }
288
289 // Function calls
290 HirExpression::FunctionCall { arguments, .. } => {
291 for arg in arguments {
292 Self::analyze_expression_internal(arg, reads);
293 }
294 }
295
296 // Field access
297 HirExpression::FieldAccess { object, .. }
298 | HirExpression::PointerFieldAccess {
299 pointer: object, ..
300 } => {
301 Self::analyze_expression_internal(object, reads);
302 }
303
304 // Array indexing
305 HirExpression::ArrayIndex { array, index }
306 | HirExpression::SliceIndex {
307 slice: array,
308 index,
309 ..
310 } => {
311 Self::analyze_expression_internal(array, reads);
312 Self::analyze_expression_internal(index, reads);
313 }
314
315 _ => {}
316 }
317 }
318}
319
320impl Default for OutputParamDetector {
321 fn default() -> Self {
322 Self::new()
323 }
324}