Skip to main content

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 { initializer: Some(expr), .. } => {
173                Self::analyze_expression_internal(expr, reads);
174            }
175
176            // Assignment can read from parameters
177            HirStatement::Assignment { value, .. } => {
178                Self::analyze_expression_internal(value, reads);
179            }
180
181            // Return statement can read from parameters
182            HirStatement::Return(Some(expr)) => {
183                Self::analyze_expression_internal(expr, reads);
184            }
185
186            // Control flow statements
187            HirStatement::If { condition, then_block, else_block } => {
188                Self::analyze_expression_internal(condition, reads);
189                for s in then_block {
190                    Self::analyze_statement_internal(s, reads, writes);
191                }
192                if let Some(else_stmts) = else_block {
193                    for s in else_stmts {
194                        Self::analyze_statement_internal(s, reads, writes);
195                    }
196                }
197            }
198
199            HirStatement::While { condition, body } => {
200                Self::analyze_expression_internal(condition, reads);
201                for s in body {
202                    Self::analyze_statement_internal(s, reads, writes);
203                }
204            }
205
206            HirStatement::For { init, condition, increment, body } => {
207                // DECY-224: Handle multiple init statements
208                for init_stmt in init {
209                    Self::analyze_statement_internal(init_stmt, reads, writes);
210                }
211                if let Some(cond) = condition {
212                    Self::analyze_expression_internal(cond, reads);
213                }
214                // DECY-224: Handle multiple increment statements
215                for inc_stmt in increment {
216                    Self::analyze_statement_internal(inc_stmt, reads, writes);
217                }
218                for s in body {
219                    Self::analyze_statement_internal(s, reads, writes);
220                }
221            }
222
223            HirStatement::Switch { condition, cases, default_case } => {
224                Self::analyze_expression_internal(condition, reads);
225                for case in cases {
226                    for s in &case.body {
227                        Self::analyze_statement_internal(s, reads, writes);
228                    }
229                }
230                if let Some(default_stmts) = default_case {
231                    for s in default_stmts {
232                        Self::analyze_statement_internal(s, reads, writes);
233                    }
234                }
235            }
236
237            HirStatement::Expression(expr) => {
238                Self::analyze_expression_internal(expr, reads);
239            }
240
241            _ => {}
242        }
243    }
244
245    /// Analyze an expression to track parameter reads.
246    fn analyze_expression_internal(
247        expr: &decy_hir::HirExpression,
248        reads: &mut HashMap<String, bool>,
249    ) {
250        use decy_hir::HirExpression;
251
252        match expr {
253            // Dereferencing a parameter is a read
254            HirExpression::Dereference(inner) => {
255                if let HirExpression::Variable(var_name) = inner.as_ref() {
256                    if reads.contains_key(var_name) {
257                        reads.insert(var_name.clone(), true);
258                    }
259                }
260            }
261
262            // Binary operations
263            HirExpression::BinaryOp { left, right, .. } => {
264                Self::analyze_expression_internal(left, reads);
265                Self::analyze_expression_internal(right, reads);
266            }
267
268            // Unary operations
269            HirExpression::UnaryOp { operand, .. } => {
270                Self::analyze_expression_internal(operand, reads);
271            }
272
273            // Function calls
274            HirExpression::FunctionCall { arguments, .. } => {
275                for arg in arguments {
276                    Self::analyze_expression_internal(arg, reads);
277                }
278            }
279
280            // Field access
281            HirExpression::FieldAccess { object, .. }
282            | HirExpression::PointerFieldAccess { pointer: object, .. } => {
283                Self::analyze_expression_internal(object, reads);
284            }
285
286            // Array indexing
287            HirExpression::ArrayIndex { array, index }
288            | HirExpression::SliceIndex { slice: array, index, .. } => {
289                Self::analyze_expression_internal(array, reads);
290                Self::analyze_expression_internal(index, reads);
291            }
292
293            _ => {}
294        }
295    }
296}
297
298impl Default for OutputParamDetector {
299    fn default() -> Self {
300        Self::new()
301    }
302}