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                if let Some(init_stmt) = init {
220                    Self::analyze_statement_internal(init_stmt, reads, writes);
221                }
222                Self::analyze_expression_internal(condition, reads);
223                if let Some(inc_stmt) = increment {
224                    Self::analyze_statement_internal(inc_stmt, reads, writes);
225                }
226                for s in body {
227                    Self::analyze_statement_internal(s, reads, writes);
228                }
229            }
230
231            HirStatement::Switch {
232                condition,
233                cases,
234                default_case,
235            } => {
236                Self::analyze_expression_internal(condition, reads);
237                for case in cases {
238                    for s in &case.body {
239                        Self::analyze_statement_internal(s, reads, writes);
240                    }
241                }
242                if let Some(default_stmts) = default_case {
243                    for s in default_stmts {
244                        Self::analyze_statement_internal(s, reads, writes);
245                    }
246                }
247            }
248
249            HirStatement::Expression(expr) => {
250                Self::analyze_expression_internal(expr, reads);
251            }
252
253            _ => {}
254        }
255    }
256
257    /// Analyze an expression to track parameter reads.
258    fn analyze_expression_internal(
259        expr: &decy_hir::HirExpression,
260        reads: &mut HashMap<String, bool>,
261    ) {
262        use decy_hir::HirExpression;
263
264        match expr {
265            // Dereferencing a parameter is a read
266            HirExpression::Dereference(inner) => {
267                if let HirExpression::Variable(var_name) = inner.as_ref() {
268                    if reads.contains_key(var_name) {
269                        reads.insert(var_name.clone(), true);
270                    }
271                }
272            }
273
274            // Binary operations
275            HirExpression::BinaryOp { left, right, .. } => {
276                Self::analyze_expression_internal(left, reads);
277                Self::analyze_expression_internal(right, reads);
278            }
279
280            // Unary operations
281            HirExpression::UnaryOp { operand, .. } => {
282                Self::analyze_expression_internal(operand, reads);
283            }
284
285            // Function calls
286            HirExpression::FunctionCall { arguments, .. } => {
287                for arg in arguments {
288                    Self::analyze_expression_internal(arg, reads);
289                }
290            }
291
292            // Field access
293            HirExpression::FieldAccess { object, .. }
294            | HirExpression::PointerFieldAccess {
295                pointer: object, ..
296            } => {
297                Self::analyze_expression_internal(object, reads);
298            }
299
300            // Array indexing
301            HirExpression::ArrayIndex { array, index }
302            | HirExpression::SliceIndex {
303                slice: array,
304                index,
305                ..
306            } => {
307                Self::analyze_expression_internal(array, reads);
308                Self::analyze_expression_internal(index, reads);
309            }
310
311            _ => {}
312        }
313    }
314}
315
316impl Default for OutputParamDetector {
317    fn default() -> Self {
318        Self::new()
319    }
320}