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 {
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}