decy_codegen/
lib.rs

1//! Rust code generation from HIR with minimal unsafe blocks.
2//!
3//! Generates idiomatic Rust code with <5 unsafe blocks per 1000 LOC.
4//!
5//! # Examples
6//!
7//! ```
8//! use decy_codegen::CodeGenerator;
9//! use decy_hir::{HirFunction, HirType, HirParameter};
10//!
11//! let func = HirFunction::new(
12//!     "add".to_string(),
13//!     HirType::Int,
14//!     vec![
15//!         HirParameter::new("a".to_string(), HirType::Int),
16//!         HirParameter::new("b".to_string(), HirType::Int),
17//!     ],
18//! );
19//!
20//! let codegen = CodeGenerator::new();
21//! let code = codegen.generate_function(&func);
22//!
23//! assert!(code.contains("fn add"));
24//! assert!(code.contains("a: i32"));
25//! assert!(code.contains("b: i32"));
26//! ```
27
28#![warn(missing_docs)]
29#![warn(clippy::all)]
30#![deny(unsafe_code)]
31
32pub mod box_transform;
33pub mod concurrency_transform;
34pub mod enum_gen;
35pub mod pattern_gen;
36pub mod test_generator;
37
38use decy_hir::{BinaryOperator, HirExpression, HirFunction, HirStatement, HirType};
39use decy_ownership::lifetime_gen::{AnnotatedSignature, AnnotatedType};
40use std::collections::HashMap;
41
42/// Type context for tracking variable types and struct definitions during code generation.
43/// Used to detect pointer arithmetic, null pointer assignments, and other type-specific operations.
44#[derive(Debug, Clone)]
45struct TypeContext {
46    variables: HashMap<String, HirType>,
47    structs: HashMap<String, Vec<(String, HirType)>>, // struct_name -> [(field_name, field_type)]
48    // DECY-117: Track function signatures for call site reference mutability
49    functions: HashMap<String, Vec<HirType>>, // func_name -> [param_types]
50    // DECY-116: Track which argument indices to skip at call sites (removed length params)
51    // func_name -> [(array_arg_index, len_arg_index_to_skip)]
52    slice_func_args: HashMap<String, Vec<(usize, usize)>>,
53    // DECY-134: Track string iteration params that use index-based access
54    // Maps param_name -> index_var_name (e.g., "dest" -> "dest_idx")
55    string_iter_params: HashMap<String, String>,
56    // DECY-134b: Track which functions have string iteration params (for call site transformation)
57    // Maps func_name -> list of (param_index, is_mutable) for string iter params
58    string_iter_funcs: HashMap<String, Vec<(usize, bool)>>,
59}
60
61impl TypeContext {
62    fn new() -> Self {
63        Self {
64            variables: HashMap::new(),
65            structs: HashMap::new(),
66            functions: HashMap::new(),
67            slice_func_args: HashMap::new(),
68            string_iter_params: HashMap::new(),
69            string_iter_funcs: HashMap::new(),
70        }
71    }
72
73    /// DECY-134b: Register a function's string iteration params for call site transformation
74    fn add_string_iter_func(&mut self, func_name: String, params: Vec<(usize, bool)>) {
75        self.string_iter_funcs.insert(func_name, params);
76    }
77
78    /// DECY-134b: Get string iteration param info for a function
79    fn get_string_iter_func(&self, func_name: &str) -> Option<&Vec<(usize, bool)>> {
80        self.string_iter_funcs.get(func_name)
81    }
82
83    /// DECY-134: Register a string iteration param with its index variable name
84    fn add_string_iter_param(&mut self, param_name: String, index_var: String) {
85        self.string_iter_params.insert(param_name, index_var);
86    }
87
88    /// DECY-134: Check if a variable is a string iteration param
89    #[allow(dead_code)] // Reserved for future use in call-site transformation
90    fn is_string_iter_param(&self, name: &str) -> bool {
91        self.string_iter_params.contains_key(name)
92    }
93
94    /// DECY-134: Get the index variable name for a string iteration param
95    fn get_string_iter_index(&self, name: &str) -> Option<&String> {
96        self.string_iter_params.get(name)
97    }
98
99    /// DECY-117: Register a function signature for call site reference mutability
100    fn add_function(&mut self, name: String, param_types: Vec<HirType>) {
101        self.functions.insert(name, param_types);
102    }
103
104    /// DECY-116: Register which args to skip at call sites for slice functions
105    fn add_slice_func_args(&mut self, name: String, arg_mappings: Vec<(usize, usize)>) {
106        self.slice_func_args.insert(name, arg_mappings);
107    }
108
109    /// DECY-116: Get the arg indices to skip for a function (length params removed)
110    fn get_slice_func_len_indices(&self, func_name: &str) -> Option<&Vec<(usize, usize)>> {
111        self.slice_func_args.get(func_name)
112    }
113
114    /// DECY-117: Get the expected parameter type for a function call
115    fn get_function_param_type(&self, func_name: &str, param_index: usize) -> Option<&HirType> {
116        self.functions
117            .get(func_name)
118            .and_then(|params| params.get(param_index))
119    }
120
121    fn from_function(func: &HirFunction) -> Self {
122        let mut ctx = Self::new();
123        // Add parameters to context
124        for param in func.parameters() {
125            ctx.variables
126                .insert(param.name().to_string(), param.param_type().clone());
127        }
128        ctx
129    }
130
131    fn add_variable(&mut self, name: String, var_type: HirType) {
132        self.variables.insert(name, var_type);
133    }
134
135    fn add_struct(&mut self, struct_def: &decy_hir::HirStruct) {
136        let fields: Vec<(String, HirType)> = struct_def
137            .fields()
138            .iter()
139            .map(|f| (f.name().to_string(), f.field_type().clone()))
140            .collect();
141        self.structs.insert(struct_def.name().to_string(), fields);
142    }
143
144    /// DECY-141: Check if a struct type implements Default (no large arrays)
145    /// Structs with arrays > 32 elements don't derive Default due to trait limitations
146    fn struct_has_default(&self, struct_name: &str) -> bool {
147        if let Some(fields) = self.structs.get(struct_name) {
148            // Check if any field has a large array (> 32 elements)
149            !fields.iter().any(|(_, field_type)| {
150                matches!(
151                    field_type,
152                    HirType::Array { size: Some(n), .. } if *n > 32
153                )
154            })
155        } else {
156            // Unknown struct - assume no Default for safety
157            false
158        }
159    }
160
161    fn get_type(&self, name: &str) -> Option<&HirType> {
162        self.variables.get(name)
163    }
164
165    fn get_field_type(&self, object_expr: &HirExpression, field_name: &str) -> Option<HirType> {
166        // Get the type of the object expression
167        let object_type = match object_expr {
168            HirExpression::Variable(var_name) => self.get_type(var_name)?,
169            _ => return None,
170        };
171
172        // Extract the struct name from the type
173        let struct_name = match object_type {
174            HirType::Struct(name) => name,
175            HirType::Pointer(inner) => {
176                // If it's a pointer to a struct, dereference it
177                if let HirType::Struct(name) = &**inner {
178                    name
179                } else {
180                    return None;
181                }
182            }
183            // DECY-115: Handle Box<Struct> for heap-allocated structs
184            HirType::Box(inner) => {
185                if let HirType::Struct(name) = &**inner {
186                    name
187                } else {
188                    return None;
189                }
190            }
191            // DECY-140: Handle Reference<Struct> for borrowed struct access
192            HirType::Reference { inner, .. } => {
193                if let HirType::Struct(name) = &**inner {
194                    name
195                } else {
196                    return None;
197                }
198            }
199            _ => return None,
200        };
201
202        // Look up the field type in the struct definition
203        let fields = self.structs.get(struct_name)?;
204        fields
205            .iter()
206            .find(|(name, _)| name == field_name)
207            .map(|(_, field_type)| field_type.clone())
208    }
209
210    fn is_pointer(&self, name: &str) -> bool {
211        matches!(self.get_type(name), Some(HirType::Pointer(_)))
212    }
213
214    fn is_option(&self, name: &str) -> bool {
215        matches!(self.get_type(name), Some(HirType::Option(_)))
216    }
217
218    fn is_vec(&self, name: &str) -> bool {
219        matches!(self.get_type(name), Some(HirType::Vec(_)))
220    }
221
222    /// Infer the type of an expression based on the context.
223    /// Returns None if the type cannot be inferred.
224    fn infer_expression_type(&self, expr: &HirExpression) -> Option<HirType> {
225        match expr {
226            HirExpression::Variable(name) => self.get_type(name).cloned(),
227            // DECY-204: Handle literal types for mixed-type arithmetic
228            HirExpression::IntLiteral(_) => Some(HirType::Int),
229            HirExpression::FloatLiteral(_) => Some(HirType::Double), // C float literals default to double
230            HirExpression::CharLiteral(_) => Some(HirType::Char),
231            HirExpression::Dereference(inner) => {
232                // If inner is *mut T, then *inner is T
233                // DECY-123: Also handle Box<T> and &T/&mut T deref → T
234                // DECY-151: Also handle Vec<T> (slice representation) deref → T
235                match self.infer_expression_type(inner) {
236                    Some(HirType::Pointer(pointee_type)) => Some(*pointee_type),
237                    Some(HirType::Box(inner_type)) => Some(*inner_type),
238                    Some(HirType::Reference {
239                        inner: ref_inner, ..
240                    }) => Some(*ref_inner),
241                    // DECY-151: Vec<T> represents slices, deref gives element type
242                    Some(HirType::Vec(elem_type)) => Some(*elem_type),
243                    _ => None,
244                }
245            }
246            HirExpression::ArrayIndex { array, index: _ } => {
247                // If array is [T; N], *mut T, or &[T], then array[i] is T
248                if let Some(array_type) = self.infer_expression_type(array) {
249                    match array_type {
250                        HirType::Array { element_type, .. } => Some(*element_type),
251                        HirType::Pointer(element_type) => Some(*element_type),
252                        // DECY-151: Handle slice types (&[T] or &mut [T])
253                        // BorrowGenerator uses Reference { inner: Vec(T) } for slices
254                        HirType::Reference { inner, .. } => match *inner {
255                            HirType::Vec(elem_type) => Some(*elem_type),
256                            HirType::Array { element_type, .. } => Some(*element_type),
257                            _ => None,
258                        },
259                        HirType::Vec(elem_type) => Some(*elem_type),
260                        _ => None,
261                    }
262                } else {
263                    None
264                }
265            }
266            // DECY-123: Handle field access to enable type inference through struct fields
267            HirExpression::FieldAccess { object, field } => {
268                // Get the struct type from the object expression
269                if let Some(obj_type) = self.infer_expression_type(object) {
270                    self.get_field_type_from_type(&obj_type, field)
271                } else {
272                    None
273                }
274            }
275            // DECY-123: Handle pointer field access (ptr->field)
276            HirExpression::PointerFieldAccess { pointer, field } => {
277                // Get the pointee type (struct) from the pointer expression
278                if let Some(ptr_type) = self.infer_expression_type(pointer) {
279                    match ptr_type {
280                        HirType::Pointer(inner) | HirType::Box(inner) => {
281                            self.get_field_type_from_type(&inner, field)
282                        }
283                        // DECY-123: Handle Reference types (&T, &mut T)
284                        HirType::Reference { inner, .. } => {
285                            self.get_field_type_from_type(&inner, field)
286                        }
287                        _ => None,
288                    }
289                } else {
290                    None
291                }
292            }
293            // DECY-210: Infer type for binary operations
294            HirExpression::BinaryOp { left, right, op } => {
295                use decy_hir::BinaryOperator;
296                let left_type = self.infer_expression_type(left);
297                let right_type = self.infer_expression_type(right);
298
299                // For arithmetic operations, follow C promotion rules
300                match op {
301                    BinaryOperator::Add
302                    | BinaryOperator::Subtract
303                    | BinaryOperator::Multiply
304                    | BinaryOperator::Divide
305                    | BinaryOperator::Modulo => {
306                        // If either operand is double, result is double
307                        if matches!(left_type, Some(HirType::Double))
308                            || matches!(right_type, Some(HirType::Double))
309                        {
310                            return Some(HirType::Double);
311                        }
312                        // If either operand is float, result is float
313                        if matches!(left_type, Some(HirType::Float))
314                            || matches!(right_type, Some(HirType::Float))
315                        {
316                            return Some(HirType::Float);
317                        }
318                        // Otherwise, result is int (char promotes to int in C)
319                        Some(HirType::Int)
320                    }
321                    // Comparison operations return bool (which we map to int for C compatibility)
322                    BinaryOperator::Equal
323                    | BinaryOperator::NotEqual
324                    | BinaryOperator::LessThan
325                    | BinaryOperator::GreaterThan
326                    | BinaryOperator::LessEqual
327                    | BinaryOperator::GreaterEqual
328                    | BinaryOperator::LogicalAnd
329                    | BinaryOperator::LogicalOr => Some(HirType::Int),
330                    // Bitwise operations return int
331                    BinaryOperator::BitwiseAnd
332                    | BinaryOperator::BitwiseOr
333                    | BinaryOperator::BitwiseXor
334                    | BinaryOperator::LeftShift
335                    | BinaryOperator::RightShift => Some(HirType::Int),
336                    _ => None,
337                }
338            }
339            _ => None,
340        }
341    }
342
343    /// DECY-123: Helper to get field type from a struct type
344    fn get_field_type_from_type(&self, obj_type: &HirType, field_name: &str) -> Option<HirType> {
345        let struct_name = match obj_type {
346            HirType::Struct(name) => name,
347            _ => return None,
348        };
349        let fields = self.structs.get(struct_name)?;
350        fields
351            .iter()
352            .find(|(name, _)| name == field_name)
353            .map(|(_, field_type)| field_type.clone())
354    }
355}
356
357/// Code generator for converting HIR to Rust source code.
358#[derive(Debug, Clone)]
359pub struct CodeGenerator {
360    box_transformer: box_transform::BoxTransformer,
361}
362
363impl CodeGenerator {
364    /// Create a new code generator.
365    ///
366    /// # Examples
367    ///
368    /// ```
369    /// use decy_codegen::CodeGenerator;
370    ///
371    /// let codegen = CodeGenerator::new();
372    /// ```
373    pub fn new() -> Self {
374        Self {
375            box_transformer: box_transform::BoxTransformer::new(),
376        }
377    }
378
379    /// DECY-143: Generate unsafe block with SAFETY comment.
380    /// All unsafe blocks should have a comment explaining why the operation is safe.
381    fn unsafe_block(code: &str, safety_reason: &str) -> String {
382        format!("/* SAFETY: {} */ unsafe {{ {} }}", safety_reason, code)
383    }
384
385    /// DECY-143: Generate unsafe statement with SAFETY comment.
386    /// For statement-level unsafe blocks (ending with semicolon).
387    fn unsafe_stmt(code: &str, safety_reason: &str) -> String {
388        format!("// SAFETY: {}\n    unsafe {{ {}; }}", safety_reason, code)
389    }
390
391    /// Generate Rust code for a macro definition.
392    ///
393    /// Transforms C #define macros to Rust const declarations (for object-like macros)
394    /// or inline functions (for function-like macros).
395    ///
396    /// # Supported Macro Types (DECY-098c)
397    ///
398    /// **Object-like macros** (constants) are fully supported:
399    /// - `#define MAX 100` → `const MAX: i32 = 100;`
400    /// - `#define PI 3.14159` → `const PI: f64 = 3.14159;`
401    /// - `#define GREETING "Hello"` → `const GREETING: &str = "Hello";`
402    ///
403    /// **Function-like macros** are not yet supported (DECY-098d):
404    /// - `#define SQR(x) ((x) * (x))` → Error
405    ///
406    /// # Type Inference
407    ///
408    /// Types are automatically inferred from the macro body:
409    /// - String literals → `&str`
410    /// - Character literals → `char`
411    /// - Floating point → `f64`
412    /// - Integers (including hex/octal) → `i32`
413    ///
414    /// # Edge Cases
415    ///
416    /// - Empty macros generate comments: `#define EMPTY` → `// Empty macro: EMPTY`
417    /// - Macro names are preserved exactly (SCREAMING_SNAKE_CASE maintained)
418    ///
419    /// # Errors
420    ///
421    /// Returns an error if:
422    /// - The macro is function-like (not yet implemented)
423    ///
424    /// # Examples
425    ///
426    /// ```
427    /// use decy_codegen::CodeGenerator;
428    /// use decy_hir::HirMacroDefinition;
429    ///
430    /// let generator = CodeGenerator::new();
431    /// let macro_def = HirMacroDefinition::new_object_like("MAX".to_string(), "100".to_string());
432    /// let rust_code = generator.generate_macro(&macro_def).unwrap();
433    /// assert!(rust_code.contains("const MAX"));
434    /// # Ok::<(), anyhow::Error>(())
435    /// ```
436    ///
437    /// # Reference
438    ///
439    /// - K&R §4.11: Macro Substitution
440    /// - ISO C99 §6.10.3: Macro replacement
441    pub fn generate_macro(
442        &self,
443        macro_def: &decy_hir::HirMacroDefinition,
444    ) -> anyhow::Result<String> {
445        if macro_def.is_function_like() {
446            // Generate inline function for function-like macros
447            return self.generate_function_like_macro(macro_def);
448        }
449
450        // Object-like macro (constant)
451        let name = macro_def.name();
452        let body = macro_def.body();
453
454        // Handle empty macros
455        if body.is_empty() {
456            return Ok(format!("// Empty macro: {}", name));
457        }
458
459        // Infer type from macro body
460        let (rust_type, rust_value) = self.infer_macro_type(body)?;
461
462        Ok(format!("const {}: {} = {};", name, rust_type, rust_value))
463    }
464
465    /// Generate Rust inline function from function-like macro.
466    ///
467    /// Transforms C function-like macros to Rust inline functions:
468    /// - `#define SQR(x) ((x) * (x))` → `fn sqr(x: i32) -> i32 { x * x }`
469    /// - `#define MAX(a, b) ((a) > (b) ? (a) : (b))` → `fn max(a: i32, b: i32) -> i32 { if a > b { a } else { b } }`
470    ///
471    /// # Features
472    ///
473    /// - Converts macro name from SCREAMING_SNAKE_CASE to snake_case
474    /// - Infers parameter types (defaults to i32)
475    /// - Infers return type from expression
476    /// - Adds `#[inline]` attribute for performance
477    /// - Transforms ternary operator (? :) to if-else
478    /// - Removes unnecessary parentheses
479    fn generate_function_like_macro(
480        &self,
481        macro_def: &decy_hir::HirMacroDefinition,
482    ) -> anyhow::Result<String> {
483        let name = macro_def.name();
484        let params = macro_def.parameters();
485        let body = macro_def.body();
486
487        // Convert SCREAMING_SNAKE_CASE to snake_case
488        let fn_name = self.convert_to_snake_case(name);
489
490        // Generate parameter list (default to i32 for now)
491        let param_list = params
492            .iter()
493            .map(|p| format!("{}: i32", p))
494            .collect::<Vec<_>>()
495            .join(", ");
496
497        // Transform macro body to Rust expression
498        let rust_body = self.transform_macro_body(body, params)?;
499
500        // Infer return type from body
501        let return_type = self.infer_return_type(body);
502
503        // Generate function
504        let result = format!(
505            "#[inline]\nfn {}({}) -> {} {{\n    {}\n}}",
506            fn_name, param_list, return_type, rust_body
507        );
508
509        Ok(result)
510    }
511
512    /// Convert SCREAMING_SNAKE_CASE to snake_case.
513    fn convert_to_snake_case(&self, name: &str) -> String {
514        name.to_lowercase()
515    }
516
517    /// Transform C macro body to Rust expression.
518    ///
519    /// Transformations:
520    /// - Remove outer parentheses: ((x) * (x)) → x * x
521    /// - Ternary operator: (a) > (b) ? (a) : (b) → if a > b { a } else { b }
522    /// - Remove parameter parentheses: (x) → x
523    fn transform_macro_body(&self, body: &str, params: &[String]) -> anyhow::Result<String> {
524        let mut result = body.to_string();
525
526        // Check for ternary operator
527        if result.contains('?') && result.contains(':') {
528            result = self.transform_ternary(&result)?;
529        } else {
530            // Remove unnecessary parentheses around parameters
531            for param in params {
532                result = result.replace(&format!("({})", param), param);
533            }
534
535            // Remove outer parentheses if present
536            result = self.remove_outer_parens(&result);
537
538            // Add spaces around operators for readability
539            result = self.add_operator_spaces(&result);
540        }
541
542        Ok(result)
543    }
544
545    /// Transform C ternary operator to Rust if-else.
546    ///
547    /// Example: ((a)>(b)?(a):(b)) → if a > b { a } else { b }
548    fn transform_ternary(&self, expr: &str) -> anyhow::Result<String> {
549        // Find the ? and : positions
550        let question_pos = expr.find('?').unwrap_or(0);
551        let colon_pos = expr.rfind(':').unwrap_or(0);
552
553        if question_pos == 0 || colon_pos == 0 || colon_pos <= question_pos {
554            // Malformed ternary, return as-is
555            return Ok(expr.to_string());
556        }
557
558        // Extract parts
559        let condition = expr[..question_pos].trim();
560        let true_expr = expr[question_pos + 1..colon_pos].trim();
561        let false_expr = expr[colon_pos + 1..].trim();
562
563        // Clean up each part
564        let condition = self.remove_outer_parens(condition);
565        let condition = self.clean_expression(&condition);
566        let true_expr = self.remove_outer_parens(true_expr);
567        let true_expr = self.clean_expression(&true_expr);
568        let false_expr = self.remove_outer_parens(false_expr);
569        let false_expr = self.clean_expression(&false_expr);
570
571        Ok(format!(
572            "if {} {{ {} }} else {{ {} }}",
573            condition, true_expr, false_expr
574        ))
575    }
576
577    /// Remove outer parentheses from expression.
578    fn remove_outer_parens(&self, expr: &str) -> String {
579        Self::remove_outer_parens_impl(expr)
580    }
581
582    /// Implementation of remove_outer_parens (recursive helper).
583    fn remove_outer_parens_impl(expr: &str) -> String {
584        let trimmed = expr.trim();
585        if trimmed.starts_with('(') && trimmed.ends_with(')') {
586            // Check if these are matching outer parens
587            let mut depth = 0;
588            let chars: Vec<char> = trimmed.chars().collect();
589            for (i, ch) in chars.iter().enumerate() {
590                match ch {
591                    '(' => depth += 1,
592                    ')' => {
593                        depth -= 1;
594                        if depth == 0 && i < chars.len() - 1 {
595                            // Found closing paren before end, not outer parens
596                            return trimmed.to_string();
597                        }
598                    }
599                    _ => {}
600                }
601            }
602            // These are outer parens, remove them
603            return Self::remove_outer_parens_impl(&trimmed[1..trimmed.len() - 1]);
604        }
605        trimmed.to_string()
606    }
607
608    /// Clean expression by removing parameter parentheses.
609    fn clean_expression(&self, expr: &str) -> String {
610        let mut result = expr.to_string();
611
612        // Handle negation: -(x) → -x (preserve the minus)
613        result = result.replace("-(x)", "-x");
614        result = result.replace("-(a)", "-a");
615        result = result.replace("-(b)", "-b");
616        result = result.replace("-(c)", "-c");
617        result = result.replace("-(n)", "-n");
618
619        // Remove parentheses around single identifiers (not negated)
620        // This is a simplified version - could be enhanced
621        result = result.replace("(x)", "x");
622        result = result.replace("(a)", "a");
623        result = result.replace("(b)", "b");
624        result = result.replace("(c)", "c");
625        result = result.replace("(n)", "n");
626
627        // Add spaces around operators
628        result = self.add_operator_spaces(&result);
629
630        result
631    }
632
633    /// Add spaces around operators for readability.
634    fn add_operator_spaces(&self, expr: &str) -> String {
635        let mut result = expr.to_string();
636
637        // Add spaces around comparison operators
638        result = result.replace(">", " > ");
639        result = result.replace("<", " < ");
640        result = result.replace("==", " == ");
641        result = result.replace("!=", " != ");
642        result = result.replace(">=", " >= ");
643        result = result.replace("<=", " <= ");
644
645        // Add spaces around logical operators (do this before arithmetic to avoid issues)
646        result = result.replace("&&", " && ");
647        result = result.replace("||", " || ");
648
649        // Add spaces around arithmetic operators
650        result = result.replace("+", " + ");
651        // Note: Don't blindly replace "-" as it could be unary minus
652        // Only replace if it's not at the start or after a space
653        let chars: Vec<char> = result.chars().collect();
654        let mut new_result = String::new();
655        for (i, ch) in chars.iter().enumerate() {
656            if *ch == '-' {
657                // Check if this is a binary minus (has non-space before it)
658                if i > 0 && !chars[i - 1].is_whitespace() && chars[i - 1] != '(' {
659                    new_result.push(' ');
660                    new_result.push(*ch);
661                    new_result.push(' ');
662                } else {
663                    // Unary minus, keep as-is
664                    new_result.push(*ch);
665                }
666            } else {
667                new_result.push(*ch);
668            }
669        }
670        result = new_result;
671
672        result = result.replace("*", " * ");
673        result = result.replace("/", " / ");
674        result = result.replace("%", " % ");
675
676        // Clean up multiple spaces
677        while result.contains("  ") {
678            result = result.replace("  ", " ");
679        }
680
681        result.trim().to_string()
682    }
683
684    /// Infer return type from macro body.
685    ///
686    /// Simple heuristic:
687    /// - Contains ternary operator (? :) → return type of branches (check for comparison at top level)
688    /// - Contains comparison operators at top level (not in ternary) → bool
689    /// - Contains logical operators (&&, ||) → bool
690    /// - Default → i32
691    fn infer_return_type(&self, body: &str) -> String {
692        // Check for ternary - return type depends on the branches, not the condition
693        if body.contains('?') && body.contains(':') {
694            // For ternary, the return type is determined by what's returned, not the condition
695            // In most C macros like MAX(a,b), the return type is i32 even though condition is bool
696            return "i32".to_string();
697        }
698
699        // Check for logical operators (&&, ||) at top level
700        if body.contains("&&") || body.contains("||") {
701            return "bool".to_string();
702        }
703
704        // Check if it's a standalone comparison (no ternary)
705        if (body.contains('>') || body.contains('<') || body.contains("==") || body.contains("!="))
706            && !body.contains('?')
707        {
708            // Standalone comparison returns bool
709            "bool".to_string()
710        } else {
711            // Default to i32
712            "i32".to_string()
713        }
714    }
715
716    /// Infer the Rust type and value from a C macro body.
717    ///
718    /// This function analyzes the macro body string and determines the appropriate
719    /// Rust type and formatted value.
720    ///
721    /// # Type Inference Rules
722    ///
723    /// - String literals (`"text"`) → `&str`
724    /// - Character literals (`'c'`) → `char`
725    /// - Floating point (contains `.` or `e`/`E`) → `f64`
726    /// - Hexadecimal (`0xFF`) → `i32` (preserves hex format)
727    /// - Octal (`0755`) → `i32` (preserves octal format)
728    /// - Integers (parseable as i32) → `i32`
729    /// - Default (expressions) → `i32`
730    ///
731    /// # Returns
732    ///
733    /// Returns a tuple of (rust_type, rust_value) where:
734    /// - `rust_type`: The Rust type as a string (e.g., "i32", "&str")
735    /// - `rust_value`: The formatted value (e.g., "100", "\"Hello\"")
736    ///
737    /// # Examples
738    ///
739    /// ```
740    /// # use decy_codegen::CodeGenerator;
741    /// let generator = CodeGenerator::new();
742    /// // This is a private method, but tested through generate_macro
743    /// # Ok::<(), anyhow::Error>(())
744    /// ```
745    fn infer_macro_type(&self, body: &str) -> anyhow::Result<(String, String)> {
746        let body = body.trim();
747
748        // String literal: "..." → &str
749        if body.starts_with('"') && body.ends_with('"') {
750            return Ok(("&str".to_string(), body.to_string()));
751        }
752
753        // Character literal: '...' → char
754        if body.starts_with('\'') && body.ends_with('\'') {
755            return Ok(("char".to_string(), body.to_string()));
756        }
757
758        // Floating point: contains '.' or 'e'/'E' → f64
759        if body.contains('.') || body.contains('e') || body.contains('E') {
760            return Ok(("f64".to_string(), body.to_string()));
761        }
762
763        // Hexadecimal: 0x... or 0X... → i32 (keep hex format)
764        if body.starts_with("0x") || body.starts_with("0X") {
765            return Ok(("i32".to_string(), body.to_string()));
766        }
767
768        // Octal: 0... → i32
769        if body.starts_with('0') && body.len() > 1 && body.chars().nth(1).unwrap().is_ascii_digit()
770        {
771            return Ok(("i32".to_string(), body.to_string()));
772        }
773
774        // Try to parse as integer (handles negative numbers too)
775        if body.parse::<i32>().is_ok() {
776            return Ok(("i32".to_string(), body.to_string()));
777        }
778
779        // Default: treat as i32 expression
780        Ok(("i32".to_string(), body.to_string()))
781    }
782
783    /// Get the Box transformer.
784    pub fn box_transformer(&self) -> &box_transform::BoxTransformer {
785        &self.box_transformer
786    }
787
788    /// Map HIR type to Rust type string.
789    ///
790    /// # Examples
791    ///
792    /// ```
793    /// use decy_codegen::CodeGenerator;
794    /// use decy_hir::HirType;
795    ///
796    /// assert_eq!(CodeGenerator::map_type(&HirType::Int), "i32");
797    /// assert_eq!(CodeGenerator::map_type(&HirType::Float), "f32");
798    /// assert_eq!(CodeGenerator::map_type(&HirType::Box(Box::new(HirType::Int))), "Box<i32>");
799    /// ```
800    pub fn map_type(hir_type: &HirType) -> String {
801        match hir_type {
802            HirType::Void => "()".to_string(),
803            HirType::Int => "i32".to_string(),
804            HirType::UnsignedInt => "u32".to_string(), // DECY-158
805            HirType::Float => "f32".to_string(),
806            HirType::Double => "f64".to_string(),
807            HirType::Char => "u8".to_string(),
808            HirType::Pointer(inner) => {
809                format!("*mut {}", Self::map_type(inner))
810            }
811            HirType::Box(inner) => {
812                format!("Box<{}>", Self::map_type(inner))
813            }
814            HirType::Vec(inner) => {
815                format!("Vec<{}>", Self::map_type(inner))
816            }
817            HirType::Option(inner) => {
818                format!("Option<{}>", Self::map_type(inner))
819            }
820            HirType::Reference { inner, mutable } => {
821                // DECY-072: Special case for slices: &Vec<T> → &[T]
822                if let HirType::Vec(element_type) = &**inner {
823                    let element_str = Self::map_type(element_type);
824                    if *mutable {
825                        format!("&mut [{}]", element_str)
826                    } else {
827                        format!("&[{}]", element_str)
828                    }
829                } else if *mutable {
830                    format!("&mut {}", Self::map_type(inner))
831                } else {
832                    format!("&{}", Self::map_type(inner))
833                }
834            }
835            HirType::Struct(name) => name.clone(),
836            HirType::Enum(name) => name.clone(),
837            HirType::Array { element_type, size } => {
838                if let Some(n) = size {
839                    format!("[{}; {}]", Self::map_type(element_type), n)
840                } else {
841                    // Unsized array - use slice reference
842                    format!("[{}]", Self::map_type(element_type))
843                }
844            }
845            HirType::FunctionPointer {
846                param_types,
847                return_type,
848            } => {
849                // C: int (*func_ptr)(int, int); → Rust: fn(i32, i32) -> i32
850                let params: Vec<String> = param_types.iter().map(Self::map_type).collect();
851                let params_str = params.join(", ");
852
853                // Skip return type annotation for void
854                if matches!(**return_type, HirType::Void) {
855                    format!("fn({})", params_str)
856                } else {
857                    format!("fn({}) -> {}", params_str, Self::map_type(return_type))
858                }
859            }
860            HirType::StringLiteral => "&str".to_string(),
861            HirType::OwnedString => "String".to_string(),
862            HirType::StringReference => "&str".to_string(),
863            HirType::Union(_) => {
864                // Unions will be transformed to Rust enums
865                // For now, return a placeholder
866                "/* Union type */".to_string()
867            }
868            // DECY-172: Preserve typedef names like size_t, ssize_t, ptrdiff_t
869            HirType::TypeAlias(name) => name.clone(),
870        }
871    }
872
873    /// Map C type name from sizeof to Rust type string.
874    ///
875    /// Handles type names as strings from sizeof expressions.
876    /// Examples: "int" → "i32", "struct Data" → "Data"
877    fn map_sizeof_type(&self, c_type_name: &str) -> String {
878        let trimmed = c_type_name.trim();
879
880        // Handle basic C types
881        match trimmed {
882            "int" => "i32".to_string(),
883            "short" | "short int" => "i16".to_string(),
884            "long" | "long int" => "i64".to_string(),
885            "long long" | "long long int" => "i64".to_string(),
886            "unsigned int" | "unsigned" => "u32".to_string(),
887            "unsigned short" | "unsigned short int" => "u16".to_string(),
888            "unsigned long" | "unsigned long int" => "u64".to_string(),
889            "unsigned long long" | "unsigned long long int" => "u64".to_string(),
890            "unsigned char" => "u8".to_string(),
891            "signed char" => "i8".to_string(),
892            "float" => "f32".to_string(),
893            "double" => "f64".to_string(),
894            "char" => "u8".to_string(),
895            "void" => "()".to_string(),
896            // Pointer types
897            "char*" | "char *" => "*mut u8".to_string(),
898            "int*" | "int *" => "*mut i32".to_string(),
899            "void*" | "void *" => "*mut ()".to_string(),
900            _ => {
901                // Handle "struct TypeName" → "TypeName"
902                if let Some(struct_name) = trimmed.strip_prefix("struct ") {
903                    struct_name.trim().to_string()
904                } else {
905                    // Keep custom type names as-is
906                    trimmed.to_string()
907                }
908            }
909        }
910    }
911
912    /// Generate code for an expression.
913    #[allow(clippy::only_used_in_recursion)]
914    pub fn generate_expression(&self, expr: &HirExpression) -> String {
915        self.generate_expression_with_context(expr, &TypeContext::new())
916    }
917
918    /// Generate code for an expression with type context for pointer arithmetic.
919    #[allow(clippy::only_used_in_recursion)]
920    fn generate_expression_with_context(&self, expr: &HirExpression, ctx: &TypeContext) -> String {
921        self.generate_expression_with_target_type(expr, ctx, None)
922    }
923
924    /// Generate code for an expression with optional target type hint for null pointer detection.
925    /// If target_type is Some(HirType::Pointer(_)) and expr is IntLiteral(0), generates std::ptr::null_mut().
926    #[allow(clippy::only_used_in_recursion)]
927    fn generate_expression_with_target_type(
928        &self,
929        expr: &HirExpression,
930        ctx: &TypeContext,
931        target_type: Option<&HirType>,
932    ) -> String {
933        match expr {
934            HirExpression::IntLiteral(val) => {
935                // Check if assigning 0 to a pointer type
936                if *val == 0 {
937                    // DECY-144: Option<Box<T>> gets None instead of null_mut
938                    if let Some(HirType::Option(_)) = target_type {
939                        return "None".to_string();
940                    }
941                    if let Some(HirType::Pointer(_)) = target_type {
942                        return "std::ptr::null_mut()".to_string();
943                    }
944                }
945                val.to_string()
946            }
947            // DECY-207: Handle float literals
948            HirExpression::FloatLiteral(val) => {
949                // Determine suffix based on target type
950                match target_type {
951                    Some(HirType::Float) => format!("{}f32", val),
952                    Some(HirType::Double) => format!("{}f64", val),
953                    _ => {
954                        // Default to f64 for double precision
955                        if val.contains('.') || val.contains('e') || val.contains('E') {
956                            format!("{}f64", val)
957                        } else {
958                            format!("{}.0f64", val)
959                        }
960                    }
961                }
962            }
963            // DECY-119: Handle AddressOf when target is raw pointer (struct field assignment)
964            // C: node.next = &x;  →  Rust: node.next = &mut x as *mut T;
965            HirExpression::AddressOf(inner) => {
966                if let Some(HirType::Pointer(ptr_inner)) = target_type {
967                    let inner_code = self.generate_expression_with_context(inner, ctx);
968                    let ptr_type = Self::map_type(&HirType::Pointer(ptr_inner.clone()));
969                    return format!("&mut {} as {}", inner_code, ptr_type);
970                }
971                // Fall through to default AddressOf handling
972                let inner_code = self.generate_expression_with_context(inner, ctx);
973                if matches!(**inner, HirExpression::Dereference(_)) {
974                    format!("&({})", inner_code)
975                } else {
976                    format!("&{}", inner_code)
977                }
978            }
979            // DECY-119: Handle UnaryOp AddressOf as well
980            HirExpression::UnaryOp {
981                op: decy_hir::UnaryOperator::AddressOf,
982                operand,
983            } => {
984                if let Some(HirType::Pointer(ptr_inner)) = target_type {
985                    let inner_code = self.generate_expression_with_context(operand, ctx);
986                    let ptr_type = Self::map_type(&HirType::Pointer(ptr_inner.clone()));
987                    return format!("&mut {} as {}", inner_code, ptr_type);
988                }
989                // Fall through to default handling
990                let inner_code = self.generate_expression_with_context(operand, ctx);
991                format!("&{}", inner_code)
992            }
993            // DECY-191: Handle LogicalNot with target type for bool-to-int coercion
994            // In C, ! returns int (0 or 1). When !(bool_expr) is assigned to int, cast to i32.
995            HirExpression::UnaryOp {
996                op: decy_hir::UnaryOperator::LogicalNot,
997                operand,
998            } => {
999                let inner_code = self.generate_expression_with_context(operand, ctx);
1000                // Wrap inner expression in parens if it's a binary op to preserve precedence
1001                let inner_parens = if matches!(**operand, HirExpression::BinaryOp { .. }) {
1002                    format!("({})", inner_code)
1003                } else {
1004                    inner_code.clone()
1005                };
1006                // If target is int, we need to cast the bool result to i32
1007                if let Some(HirType::Int) = target_type {
1008                    if Self::is_boolean_expression(operand) {
1009                        // !bool_expr returns bool, needs cast to i32
1010                        return format!("(!{}) as i32", inner_parens);
1011                    } else {
1012                        // !int_expr becomes (int == 0) which is bool, then cast to i32
1013                        return format!("({} == 0) as i32", inner_code);
1014                    }
1015                }
1016                // No target type or non-int target - use boolean result (no cast)
1017                // The as i32 cast is only needed when assigning to int variable
1018                if Self::is_boolean_expression(operand) {
1019                    format!("!{}", inner_parens)
1020                } else {
1021                    // !int_expr becomes (int == 0) which is bool - no cast needed
1022                    format!("({} == 0)", inner_code)
1023                }
1024            }
1025            HirExpression::StringLiteral(s) => {
1026                // DECY-212: Handle string literal to pointer conversion
1027                // When returning/assigning string literal to *mut u8 or *const u8, convert properly
1028                if let Some(HirType::Pointer(inner)) = target_type {
1029                    if matches!(inner.as_ref(), HirType::Char) {
1030                        // Return as byte string pointer: b"...\0".as_ptr() as *mut u8
1031                        let escaped: String = s
1032                            .chars()
1033                            .map(|c| match c {
1034                                '"' => "\\\"".to_string(),
1035                                '\\' => "\\\\".to_string(),
1036                                c => c.to_string(),
1037                            })
1038                            .collect();
1039                        return format!("b\"{}\\0\".as_ptr() as *mut u8", escaped);
1040                    }
1041                }
1042                format!("\"{}\"", s)
1043            }
1044            HirExpression::CharLiteral(c) => {
1045                // For char literals, convert to u8 equivalent
1046                // '\0' = 0, 'a' = 97, etc.
1047                let val = *c as u8;
1048                if val == 0 {
1049                    "0u8".to_string()
1050                } else if val.is_ascii_graphic() || val == b' ' {
1051                    format!("b'{}'", val as char)
1052                } else {
1053                    // For non-printable characters, use the numeric value
1054                    format!("{}u8", val)
1055                }
1056            }
1057            HirExpression::Variable(name) => {
1058                // DECY-142: Vec to Vec - return directly (no conversion needed)
1059                // When target type is Vec<T> and variable is Vec<T>, return as-is
1060                if let Some(HirType::Vec(_)) = target_type {
1061                    // Variable being returned in Vec-return context - return directly
1062                    return name.clone();
1063                }
1064                // DECY-115: Box to raw pointer conversion for return statements
1065                // When returning a Box<T> but function returns *mut T, use Box::into_raw
1066                if let Some(HirType::Pointer(ptr_inner)) = target_type {
1067                    if let Some(var_type) = ctx.get_type(name) {
1068                        if matches!(var_type, HirType::Box(_)) {
1069                            return format!("Box::into_raw({})", name);
1070                        }
1071                        // DECY-118/DECY-146: Reference/Slice to raw pointer coercion
1072                        // &[T] or &mut [T] assigned to *mut T needs .as_ptr() / .as_mut_ptr()
1073                        // &T or &mut T assigned to *mut T needs coercion (pointer cast)
1074                        match var_type {
1075                            HirType::Reference { inner, mutable } => {
1076                                // DECY-149: Check if inner is an array/slice or Vec (both represent slices)
1077                                // BorrowGenerator uses Vec as internal representation for slices
1078                                let element_type_match = match inner.as_ref() {
1079                                    HirType::Array { element_type, .. } => {
1080                                        Some((element_type.as_ref(), *mutable))
1081                                    }
1082                                    HirType::Vec(elem_type) => Some((elem_type.as_ref(), *mutable)),
1083                                    _ => None,
1084                                };
1085
1086                                if let Some((elem_type, is_mutable)) = element_type_match {
1087                                    // Slice: verify element types match
1088                                    if elem_type == ptr_inner.as_ref() {
1089                                        if is_mutable {
1090                                            // Mutable slice: use .as_mut_ptr()
1091                                            return format!("{}.as_mut_ptr()", name);
1092                                        } else {
1093                                            // Immutable slice: use .as_ptr() with cast
1094                                            let ptr_type = Self::map_type(&HirType::Pointer(
1095                                                ptr_inner.clone(),
1096                                            ));
1097                                            return format!("{}.as_ptr() as {}", name, ptr_type);
1098                                        }
1099                                    }
1100                                } else if inner.as_ref() == ptr_inner.as_ref() {
1101                                    // DECY-146: Single reference (&T or &mut T) to pointer
1102                                    // Cast using addr_of!/addr_of_mut! or pointer cast
1103                                    if *mutable {
1104                                        return format!("{} as *mut _", name);
1105                                    } else {
1106                                        return format!("{} as *const _ as *mut _", name);
1107                                    }
1108                                }
1109                            }
1110                            // Also handle Vec<T> to *mut T
1111                            HirType::Vec(elem_type) => {
1112                                if elem_type.as_ref() == ptr_inner.as_ref() {
1113                                    return format!("{}.as_mut_ptr()", name);
1114                                }
1115                            }
1116                            // DECY-211: Handle Array[T; N] to *mut T
1117                            // In C, arrays decay to pointers when assigned to pointer variables
1118                            // In Rust: arr.as_mut_ptr()
1119                            HirType::Array { element_type, .. } => {
1120                                if element_type.as_ref() == ptr_inner.as_ref() {
1121                                    return format!("{}.as_mut_ptr()", name);
1122                                }
1123                            }
1124                            // DECY-148: Handle Pointer(T) → Pointer(T)
1125                            // When context has raw pointer type, just return the variable directly
1126                            // No conversion needed - it's already a raw pointer!
1127                            HirType::Pointer(_var_inner) => {
1128                                // Raw pointer stays as raw pointer - just return it
1129                                return name.clone();
1130                            }
1131                            _ => {}
1132                        }
1133                    }
1134                }
1135
1136                // DECY-198: Handle int to char coercion
1137                // In C, assigning int to char array element truncates: s[i] = c (c is int)
1138                // In Rust, need explicit cast: s[i] = c as u8
1139                if let Some(HirType::Char) = target_type {
1140                    if let Some(var_type) = ctx.get_type(name) {
1141                        if matches!(var_type, HirType::Int) {
1142                            return format!("{} as u8", name);
1143                        }
1144                    }
1145                }
1146
1147                // DECY-203: Handle numeric type coercions (int/float/double)
1148                // C allows implicit conversions between numeric types
1149                if let Some(target) = target_type {
1150                    if let Some(var_type) = ctx.get_type(name) {
1151                        // Int to Float/Double
1152                        if matches!(var_type, HirType::Int | HirType::UnsignedInt) {
1153                            if matches!(target, HirType::Float) {
1154                                return format!("{} as f32", name);
1155                            } else if matches!(target, HirType::Double) {
1156                                return format!("{} as f64", name);
1157                            }
1158                        }
1159                        // Float/Double to Int (truncation)
1160                        if matches!(var_type, HirType::Float | HirType::Double) {
1161                            if matches!(target, HirType::Int) {
1162                                return format!("{} as i32", name);
1163                            } else if matches!(target, HirType::UnsignedInt) {
1164                                return format!("{} as u32", name);
1165                            }
1166                        }
1167                        // Char to Int
1168                        if matches!(var_type, HirType::Char) && matches!(target, HirType::Int) {
1169                            return format!("{} as i32", name);
1170                        }
1171                    }
1172                }
1173
1174                name.clone()
1175            }
1176            HirExpression::BinaryOp { op, left, right } => {
1177                // DECY-195: Handle embedded assignment expressions
1178                // In C, (c = getchar()) evaluates to the assigned value
1179                // In Rust, assignment returns (), so we need a block: { let tmp = rhs; lhs = tmp; tmp }
1180                if matches!(op, BinaryOperator::Assign) {
1181                    let left_code = self.generate_expression_with_context(left, ctx);
1182                    let right_code = self.generate_expression_with_context(right, ctx);
1183                    return format!(
1184                        "{{ let __assign_tmp = {}; {} = __assign_tmp; __assign_tmp }}",
1185                        right_code, left_code
1186                    );
1187                }
1188
1189                // Check for Option comparison with NULL → is_none() / is_some()
1190                // p == NULL → p.is_none(), p != NULL → p.is_some()
1191                if matches!(op, BinaryOperator::Equal | BinaryOperator::NotEqual) {
1192                    // Check if left is an Option and right is NULL
1193                    if let HirExpression::Variable(var_name) = &**left {
1194                        if ctx.is_option(var_name) && matches!(**right, HirExpression::NullLiteral)
1195                        {
1196                            return match op {
1197                                BinaryOperator::Equal => format!("{}.is_none()", var_name),
1198                                BinaryOperator::NotEqual => format!("{}.is_some()", var_name),
1199                                _ => unreachable!(),
1200                            };
1201                        }
1202                    }
1203                    // Check if right is an Option and left is NULL (NULL == p or NULL != p)
1204                    if let HirExpression::Variable(var_name) = &**right {
1205                        if ctx.is_option(var_name) && matches!(**left, HirExpression::NullLiteral) {
1206                            return match op {
1207                                BinaryOperator::Equal => format!("{}.is_none()", var_name),
1208                                BinaryOperator::NotEqual => format!("{}.is_some()", var_name),
1209                                _ => unreachable!(),
1210                            };
1211                        }
1212                    }
1213
1214                    // Check for pointer comparison with 0 (null pointer comparison)
1215                    // ptr == 0 or ptr != 0 should become ptr == std::ptr::null_mut() or ptr != std::ptr::null_mut()
1216                    // Check if left is a pointer and right is 0
1217                    if let HirExpression::Variable(var_name) = &**left {
1218                        if ctx.is_pointer(var_name) {
1219                            if let HirExpression::IntLiteral(0) = **right {
1220                                let op_str = Self::binary_operator_to_string(op);
1221                                return format!("{} {} std::ptr::null_mut()", var_name, op_str);
1222                            }
1223                        }
1224                    }
1225                    // Check if right is a pointer and left is 0 (0 == ptr or 0 != ptr)
1226                    if let HirExpression::Variable(var_name) = &**right {
1227                        if ctx.is_pointer(var_name) {
1228                            if let HirExpression::IntLiteral(0) = **left {
1229                                let op_str = Self::binary_operator_to_string(op);
1230                                return format!("std::ptr::null_mut() {} {}", op_str, var_name);
1231                            }
1232                        }
1233                    }
1234
1235                    // DECY-130: Vec null check → always false (Vec allocation never fails in safe Rust)
1236                    // arr == 0 or arr == NULL for Vec types should become `false` (or removed)
1237                    // because vec![] never returns null - it panics on OOM instead
1238                    if let HirExpression::Variable(var_name) = &**left {
1239                        if ctx.is_vec(var_name)
1240                            && matches!(
1241                                **right,
1242                                HirExpression::IntLiteral(0) | HirExpression::NullLiteral
1243                            )
1244                        {
1245                            return match op {
1246                                BinaryOperator::Equal => "false /* Vec never null */".to_string(),
1247                                BinaryOperator::NotEqual => "true /* Vec never null */".to_string(),
1248                                _ => unreachable!(),
1249                            };
1250                        }
1251                    }
1252
1253                    // DECY-119: Box null check → always true/false (Box allocation never fails)
1254                    // Similar to Vec, Box::new() never returns null - it panics on OOM
1255                    if let HirExpression::Variable(var_name) = &**left {
1256                        if let Some(HirType::Box(_)) = ctx.get_type(var_name) {
1257                            if matches!(
1258                                **right,
1259                                HirExpression::IntLiteral(0) | HirExpression::NullLiteral
1260                            ) {
1261                                return match op {
1262                                    BinaryOperator::Equal => {
1263                                        "false /* Box never null */".to_string()
1264                                    }
1265                                    BinaryOperator::NotEqual => {
1266                                        "true /* Box never null */".to_string()
1267                                    }
1268                                    _ => unreachable!(),
1269                                };
1270                            }
1271                        }
1272                    }
1273
1274                    // DECY-199: strlen(s) == 0 → s.is_empty() or s.len() == 0
1275                    // This is more idiomatic Rust than s.len() as i32 == 0
1276                    if let HirExpression::FunctionCall { function, arguments } = &**left {
1277                        if function == "strlen" && arguments.len() == 1 {
1278                            if let HirExpression::IntLiteral(0) = **right {
1279                                let arg_code = self.generate_expression_with_context(&arguments[0], ctx);
1280                                return match op {
1281                                    BinaryOperator::Equal => format!("{}.is_empty()", arg_code),
1282                                    BinaryOperator::NotEqual => format!("!{}.is_empty()", arg_code),
1283                                    _ => unreachable!(),
1284                                };
1285                            }
1286                        }
1287                    }
1288                    // Also handle 0 == strlen(s)
1289                    if let HirExpression::FunctionCall { function, arguments } = &**right {
1290                        if function == "strlen" && arguments.len() == 1 {
1291                            if let HirExpression::IntLiteral(0) = **left {
1292                                let arg_code = self.generate_expression_with_context(&arguments[0], ctx);
1293                                return match op {
1294                                    BinaryOperator::Equal => format!("{}.is_empty()", arg_code),
1295                                    BinaryOperator::NotEqual => format!("!{}.is_empty()", arg_code),
1296                                    _ => unreachable!(),
1297                                };
1298                            }
1299                        }
1300                    }
1301                }
1302
1303                // DECY-198: Handle char literal to int coercion in comparisons
1304                // In C, char literals are promoted to int when compared with int variables
1305                // e.g., c != '\n' where c is int should compare against 10 (not 10u8)
1306                let is_comparison = matches!(
1307                    op,
1308                    BinaryOperator::Equal
1309                        | BinaryOperator::NotEqual
1310                        | BinaryOperator::LessThan
1311                        | BinaryOperator::GreaterThan
1312                        | BinaryOperator::LessEqual
1313                        | BinaryOperator::GreaterEqual
1314                );
1315
1316                if is_comparison {
1317                    // Check if left is int variable and right is char literal
1318                    if let HirExpression::Variable(var_name) = &**left {
1319                        if let Some(HirType::Int) = ctx.get_type(var_name) {
1320                            if let HirExpression::CharLiteral(c) = &**right {
1321                                let left_code = self.generate_expression_with_context(left, ctx);
1322                                let op_str = Self::binary_operator_to_string(op);
1323                                // Generate char as i32 literal
1324                                return format!("({} {} {}i32)", left_code, op_str, *c as i32);
1325                            }
1326                        }
1327                    }
1328                    // Check if right is int variable and left is char literal
1329                    if let HirExpression::Variable(var_name) = &**right {
1330                        if let Some(HirType::Int) = ctx.get_type(var_name) {
1331                            if let HirExpression::CharLiteral(c) = &**left {
1332                                let right_code = self.generate_expression_with_context(right, ctx);
1333                                let op_str = Self::binary_operator_to_string(op);
1334                                // Generate char as i32 literal
1335                                return format!("({}i32 {} {})", *c as i32, op_str, right_code);
1336                            }
1337                        }
1338                    }
1339                }
1340
1341                // DECY-210: Handle integer + char literal arithmetic
1342                // In C, 'c' has type int, so (n % 10) + '0' is int + int = int
1343                // In Rust, b'0' is u8, so we need to cast it to i32
1344                if matches!(op, BinaryOperator::Add | BinaryOperator::Subtract) {
1345                    // Check if right is char literal
1346                    if let HirExpression::CharLiteral(c) = &**right {
1347                        let left_type = ctx.infer_expression_type(left);
1348                        if matches!(left_type, Some(HirType::Int)) {
1349                            let left_code = self.generate_expression_with_context(left, ctx);
1350                            let op_str = Self::binary_operator_to_string(op);
1351                            return format!("({} {} {}i32)", left_code, op_str, *c as i32);
1352                        }
1353                    }
1354                    // Check if left is char literal
1355                    if let HirExpression::CharLiteral(c) = &**left {
1356                        let right_type = ctx.infer_expression_type(right);
1357                        if matches!(right_type, Some(HirType::Int)) {
1358                            let right_code = self.generate_expression_with_context(right, ctx);
1359                            let op_str = Self::binary_operator_to_string(op);
1360                            return format!("({}i32 {} {})", *c as i32, op_str, right_code);
1361                        }
1362                    }
1363                }
1364
1365                let left_code = self.generate_expression_with_context(left, ctx);
1366                let right_code = self.generate_expression_with_context(right, ctx);
1367                let op_str = Self::binary_operator_to_string(op);
1368
1369                // Add parentheses for nested binary operations
1370                let left_str = if matches!(**left, HirExpression::BinaryOp { .. }) {
1371                    format!("({})", left_code)
1372                } else {
1373                    left_code.clone()
1374                };
1375
1376                let right_str = if matches!(**right, HirExpression::BinaryOp { .. }) {
1377                    format!("({})", right_code)
1378                } else {
1379                    right_code.clone()
1380                };
1381
1382                // DECY-041: Detect pointer arithmetic using type context
1383                if matches!(op, BinaryOperator::Add | BinaryOperator::Subtract) {
1384                    if let HirExpression::Variable(var_name) = &**left {
1385                        if ctx.is_pointer(var_name) {
1386                            // This is pointer arithmetic - generate pointer method calls
1387                            // Note: wrapping_add/wrapping_sub are safe methods on raw pointers
1388                            // Only offset_from needs unsafe
1389                            return match op {
1390                                BinaryOperator::Add => {
1391                                    format!("{}.wrapping_add({} as usize)", left_str, right_str)
1392                                }
1393                                BinaryOperator::Subtract => {
1394                                    // Check if right is also a pointer (ptr - ptr) or integer (ptr - offset)
1395                                    if let HirExpression::Variable(right_var) = &**right {
1396                                        if ctx.is_pointer(right_var) {
1397                                            // ptr - ptr: calculate difference (returns isize, cast to i32 for C compatibility)
1398                                            // offset_from requires unsafe
1399                                            // DECY-143: Add SAFETY comment
1400                                            Self::unsafe_block(
1401                                                &format!(
1402                                                    "{}.offset_from({}) as i32",
1403                                                    left_str, right_str
1404                                                ),
1405                                                "both pointers derive from same allocation",
1406                                            )
1407                                        } else {
1408                                            // ptr - integer offset (safe)
1409                                            format!(
1410                                                "{}.wrapping_sub({} as usize)",
1411                                                left_str, right_str
1412                                            )
1413                                        }
1414                                    } else {
1415                                        // ptr - integer offset (literal or expression, safe)
1416                                        format!("{}.wrapping_sub({} as usize)", left_str, right_str)
1417                                    }
1418                                }
1419                                _ => unreachable!(),
1420                            };
1421                        }
1422                    }
1423                }
1424
1425                // DECY-131: Handle logical operators with integer operands
1426                // In C, non-zero integers are truthy. In Rust, we need explicit conversion.
1427                if matches!(op, BinaryOperator::LogicalAnd | BinaryOperator::LogicalOr) {
1428                    // Check if operands are likely integers (not already boolean comparisons)
1429                    let left_needs_bool = !Self::is_boolean_expression(left);
1430                    let right_needs_bool = !Self::is_boolean_expression(right);
1431
1432                    let left_bool = if left_needs_bool {
1433                        format!("({} != 0)", left_str)
1434                    } else {
1435                        left_str.clone()
1436                    };
1437
1438                    let right_bool = if right_needs_bool {
1439                        format!("({} != 0)", right_str)
1440                    } else {
1441                        right_str.clone()
1442                    };
1443
1444                    // DECY-191: If target type is Int, cast the bool result to i32
1445                    // In C, logical operators return int (0 or 1), not bool
1446                    if let Some(HirType::Int) = target_type {
1447                        return format!("({} {} {}) as i32", left_bool, op_str, right_bool);
1448                    }
1449
1450                    return format!("{} {} {}", left_bool, op_str, right_bool);
1451                }
1452
1453                // DECY-151: Char to int promotion for arithmetic operations
1454                // In C, char arithmetic like *s1 - *s2 is auto-promoted to int
1455                // When target type is i32 and operands are char (u8), cast to i32
1456                if matches!(
1457                    op,
1458                    BinaryOperator::Add
1459                        | BinaryOperator::Subtract
1460                        | BinaryOperator::Multiply
1461                        | BinaryOperator::Divide
1462                        | BinaryOperator::Modulo
1463                ) {
1464                    // Check if target type is Int and operands might be Char
1465                    if let Some(HirType::Int) = target_type {
1466                        // Infer operand types
1467                        let left_type = ctx.infer_expression_type(left);
1468                        let right_type = ctx.infer_expression_type(right);
1469
1470                        // If either operand is Char (u8), cast both to i32 for proper arithmetic
1471                        let left_is_char = matches!(left_type, Some(HirType::Char));
1472                        let right_is_char = matches!(right_type, Some(HirType::Char));
1473
1474                        if left_is_char || right_is_char {
1475                            let left_cast = if left_is_char {
1476                                format!("({} as i32)", left_str)
1477                            } else {
1478                                left_str.clone()
1479                            };
1480                            let right_cast = if right_is_char {
1481                                format!("({} as i32)", right_str)
1482                            } else {
1483                                right_str.clone()
1484                            };
1485                            return format!("{} {} {}", left_cast, op_str, right_cast);
1486                        }
1487                    }
1488
1489                    // DECY-204: Handle mixed int/float/double arithmetic
1490                    // In C, int + float promotes int to float, int + double promotes int to double
1491                    let left_type = ctx.infer_expression_type(left);
1492                    let right_type = ctx.infer_expression_type(right);
1493
1494                    let left_is_int = matches!(left_type, Some(HirType::Int) | Some(HirType::UnsignedInt));
1495                    let right_is_int = matches!(right_type, Some(HirType::Int) | Some(HirType::UnsignedInt));
1496                    let left_is_float = matches!(left_type, Some(HirType::Float));
1497                    let right_is_float = matches!(right_type, Some(HirType::Float));
1498                    let left_is_double = matches!(left_type, Some(HirType::Double));
1499                    let right_is_double = matches!(right_type, Some(HirType::Double));
1500
1501                    // int + float or float + int → cast int to f32
1502                    if (left_is_int && right_is_float) || (left_is_float && right_is_int) {
1503                        let left_cast = if left_is_int {
1504                            format!("({} as f32)", left_str)
1505                        } else {
1506                            left_str.clone()
1507                        };
1508                        let right_cast = if right_is_int {
1509                            format!("({} as f32)", right_str)
1510                        } else {
1511                            right_str.clone()
1512                        };
1513                        return format!("{} {} {}", left_cast, op_str, right_cast);
1514                    }
1515
1516                    // int + double or double + int → cast int to f64
1517                    if (left_is_int && right_is_double) || (left_is_double && right_is_int) {
1518                        let left_cast = if left_is_int {
1519                            format!("({} as f64)", left_str)
1520                        } else {
1521                            left_str.clone()
1522                        };
1523                        let right_cast = if right_is_int {
1524                            format!("({} as f64)", right_str)
1525                        } else {
1526                            right_str.clone()
1527                        };
1528                        return format!("{} {} {}", left_cast, op_str, right_cast);
1529                    }
1530
1531                    // float + double or double + float → cast float to f64
1532                    if (left_is_float && right_is_double) || (left_is_double && right_is_float) {
1533                        let left_cast = if left_is_float {
1534                            format!("({} as f64)", left_str)
1535                        } else {
1536                            left_str.clone()
1537                        };
1538                        let right_cast = if right_is_float {
1539                            format!("({} as f64)", right_str)
1540                        } else {
1541                            right_str.clone()
1542                        };
1543                        return format!("{} {} {}", left_cast, op_str, right_cast);
1544                    }
1545                }
1546
1547                // DECY-191: Comparison and logical operators return bool in Rust but int in C
1548                // When assigning to an integer type, cast the result to i32
1549                let returns_bool = matches!(
1550                    op,
1551                    BinaryOperator::GreaterThan
1552                        | BinaryOperator::LessThan
1553                        | BinaryOperator::GreaterEqual
1554                        | BinaryOperator::LessEqual
1555                        | BinaryOperator::Equal
1556                        | BinaryOperator::NotEqual
1557                        | BinaryOperator::LogicalAnd
1558                        | BinaryOperator::LogicalOr
1559                );
1560
1561                // DECY-206: Handle chained comparisons - (x < y) < z
1562                // In C, comparisons return int (0 or 1), so this is valid
1563                // In Rust, comparisons return bool, can't compare with int
1564                // Fix: Cast comparison operands to i32 when used in further comparisons
1565                if is_comparison {
1566                    // Check if left operand is a comparison (produces bool in Rust)
1567                    let left_is_comparison = Self::is_boolean_expression(left);
1568                    let right_is_comparison = Self::is_boolean_expression(right);
1569
1570                    if left_is_comparison || right_is_comparison {
1571                        // Wrap casts in parens to avoid Rust parsing `i32 < z` as generics
1572                        let left_code = if left_is_comparison {
1573                            format!("(({}) as i32)", left_str)
1574                        } else {
1575                            left_str.clone()
1576                        };
1577                        let right_code = if right_is_comparison {
1578                            format!("(({}) as i32)", right_str)
1579                        } else {
1580                            right_str.clone()
1581                        };
1582                        // Result is still a comparison, but operands are now both int
1583                        if let Some(HirType::Int) = target_type {
1584                            return format!("({} {} {}) as i32", left_code, op_str, right_code);
1585                        }
1586                        return format!("{} {} {}", left_code, op_str, right_code);
1587                    }
1588                }
1589
1590                if returns_bool {
1591                    if let Some(HirType::Int) = target_type {
1592                        // Wrap in parentheses and cast to i32
1593                        return format!("({} {} {}) as i32", left_str, op_str, right_str);
1594                    }
1595                }
1596
1597                // DECY-204: Cast arithmetic result to target type if needed
1598                // e.g., int / int = int, but if target is float, cast to float
1599                if matches!(
1600                    op,
1601                    BinaryOperator::Add
1602                        | BinaryOperator::Subtract
1603                        | BinaryOperator::Multiply
1604                        | BinaryOperator::Divide
1605                        | BinaryOperator::Modulo
1606                ) {
1607                    // Infer result type of the operation
1608                    let left_type = ctx.infer_expression_type(left);
1609                    let right_type = ctx.infer_expression_type(right);
1610
1611                    // If both operands are int, result is int
1612                    let result_is_int = matches!(left_type, Some(HirType::Int) | Some(HirType::UnsignedInt))
1613                        && matches!(right_type, Some(HirType::Int) | Some(HirType::UnsignedInt));
1614
1615                    // If target is float/double but result is int, cast the result
1616                    if result_is_int {
1617                        if let Some(HirType::Float) = target_type {
1618                            return format!("({} {} {}) as f32", left_str, op_str, right_str);
1619                        }
1620                        if let Some(HirType::Double) = target_type {
1621                            return format!("({} {} {}) as f64", left_str, op_str, right_str);
1622                        }
1623                    }
1624                }
1625
1626                format!("{} {} {}", left_str, op_str, right_str)
1627            }
1628            HirExpression::Dereference(inner) => {
1629                // DECY-134: Check for string iteration param - use slice indexing
1630                if let HirExpression::Variable(var_name) = &**inner {
1631                    if let Some(idx_var) = ctx.get_string_iter_index(var_name) {
1632                        // Transform *ptr to slice[idx] - no unsafe needed!
1633                        return format!("{}[{}]", var_name, idx_var);
1634                    }
1635
1636                    // DECY-138: Check for &str type - use as_bytes()[0] for dereference
1637                    // C pattern: *str where str is const char*
1638                    // Rust pattern: str.as_bytes()[0] as i32 (cast for integer compatibility)
1639                    if let Some(var_type) = ctx.get_type(var_name) {
1640                        if matches!(var_type, HirType::StringReference | HirType::StringLiteral) {
1641                            return format!("{}.as_bytes()[0] as i32", var_name);
1642                        }
1643                    }
1644                }
1645
1646                // DECY-138: Check for *str++ pattern - PostIncrement on &str already returns byte
1647                // Don't add extra dereference when inner is PostIncrement on &str
1648                if let HirExpression::PostIncrement { operand } = &**inner {
1649                    if let HirExpression::Variable(var_name) = &**operand {
1650                        if let Some(var_type) = ctx.get_type(var_name) {
1651                            if matches!(var_type, HirType::StringReference | HirType::StringLiteral)
1652                            {
1653                                // PostIncrement on &str already generates the byte value
1654                                // No extra dereference needed
1655                                return self.generate_expression_with_context(inner, ctx);
1656                            }
1657                        }
1658                    }
1659                }
1660
1661                let inner_code = self.generate_expression_with_context(inner, ctx);
1662
1663                // DECY-041: Check if dereferencing a raw pointer - if so, wrap in unsafe
1664                if let HirExpression::Variable(var_name) = &**inner {
1665                    if ctx.is_pointer(var_name) {
1666                        // DECY-143: Add SAFETY comment
1667                        return Self::unsafe_block(
1668                            &format!("*{}", inner_code),
1669                            "pointer is valid and properly aligned from caller contract",
1670                        );
1671                    }
1672                }
1673
1674                format!("*{}", inner_code)
1675            }
1676            // Note: HirExpression::AddressOf is handled earlier in this match with target_type awareness
1677            HirExpression::UnaryOp { op, operand } => {
1678                use decy_hir::UnaryOperator;
1679                match op {
1680                    // Post-increment: x++ → { let tmp = x; x += 1; tmp }
1681                    // Returns old value before incrementing
1682                    UnaryOperator::PostIncrement => {
1683                        let operand_code = self.generate_expression_with_context(operand, ctx);
1684                        format!(
1685                            "{{ let tmp = {}; {} += 1; tmp }}",
1686                            operand_code, operand_code
1687                        )
1688                    }
1689                    // Post-decrement: x-- → { let tmp = x; x -= 1; tmp }
1690                    // Returns old value before decrementing
1691                    UnaryOperator::PostDecrement => {
1692                        let operand_code = self.generate_expression_with_context(operand, ctx);
1693                        format!(
1694                            "{{ let tmp = {}; {} -= 1; tmp }}",
1695                            operand_code, operand_code
1696                        )
1697                    }
1698                    // Pre-increment: ++x → { x += 1; x }
1699                    // Increments first, then returns new value
1700                    UnaryOperator::PreIncrement => {
1701                        let operand_code = self.generate_expression_with_context(operand, ctx);
1702                        format!("{{ {} += 1; {} }}", operand_code, operand_code)
1703                    }
1704                    // Pre-decrement: --x → { x -= 1; x }
1705                    // Decrements first, then returns new value
1706                    UnaryOperator::PreDecrement => {
1707                        let operand_code = self.generate_expression_with_context(operand, ctx);
1708                        format!("{{ {} -= 1; {} }}", operand_code, operand_code)
1709                    }
1710                    // DECY-131, DECY-191: Logical NOT on integer → (x == 0) as i32
1711                    // In C, ! returns int (0 or 1), not bool. This matters when !x is used
1712                    // in expressions like !a == b where we compare the result with an int.
1713                    UnaryOperator::LogicalNot => {
1714                        let operand_code = self.generate_expression_with_context(operand, ctx);
1715                        // If operand is already boolean, just negate it
1716                        if Self::is_boolean_expression(operand) {
1717                            format!("!{}", operand_code)
1718                        } else {
1719                            // For integers: !x → (x == 0) as i32 to match C semantics
1720                            // where ! returns int, enabling expressions like !a == b
1721                            format!("({} == 0) as i32", operand_code)
1722                        }
1723                    }
1724                    // Simple prefix operators
1725                    _ => {
1726                        let op_str = Self::unary_operator_to_string(op);
1727                        let operand_code = self.generate_expression_with_context(operand, ctx);
1728                        format!("{}{}", op_str, operand_code)
1729                    }
1730                }
1731            }
1732            HirExpression::FunctionCall {
1733                function,
1734                arguments,
1735            } => {
1736                // Special handling for standard library functions
1737                match function.as_str() {
1738                    // strlen(s) → s.len() as i32
1739                    // Reference: K&R §B3, ISO C99 §7.21.6.3
1740                    // DECY-199: Cast to i32 since strlen result is often used in int arithmetic
1741                    "strlen" => {
1742                        if arguments.len() == 1 {
1743                            format!(
1744                                "{}.len() as i32",
1745                                self.generate_expression_with_context(&arguments[0], ctx)
1746                            )
1747                        } else {
1748                            // Invalid strlen call - shouldn't happen, but handle gracefully
1749                            let args: Vec<String> = arguments
1750                                .iter()
1751                                .map(|arg| self.generate_expression_with_context(arg, ctx))
1752                                .collect();
1753                            format!("{}({})", function, args.join(", "))
1754                        }
1755                    }
1756                    // strcpy(dest, src) → CStr-based copy or .to_string()
1757                    // Reference: K&R §B3, ISO C99 §7.21.3.1
1758                    // strcpy copies src to dest and returns dest pointer.
1759                    // DECY-188: Use CStr for raw pointer sources, .to_string() for &str
1760                    "strcpy" => {
1761                        if arguments.len() == 2 {
1762                            let src_code = self.generate_expression_with_context(&arguments[1], ctx);
1763                            // DECY-188: Detect if source looks like a raw pointer dereference
1764                            // Patterns like (*foo).bar or (*foo) indicate raw pointer access
1765                            // Simple variable names that aren't dereferenced are likely &str
1766                            let is_raw_pointer = src_code.contains("(*") ||
1767                                                 src_code.contains(").") ||
1768                                                 src_code.contains("as *");
1769                            if is_raw_pointer {
1770                                format!(
1771                                    "unsafe {{ std::ffi::CStr::from_ptr({} as *const i8).to_str().unwrap_or(\"\").to_string() }}",
1772                                    src_code
1773                                )
1774                            } else {
1775                                // &str source - use direct .to_string()
1776                                format!("{}.to_string()", src_code)
1777                            }
1778                        } else {
1779                            // Invalid strcpy call - shouldn't happen, but handle gracefully
1780                            let args: Vec<String> = arguments
1781                                .iter()
1782                                .map(|arg| self.generate_expression_with_context(arg, ctx))
1783                                .collect();
1784                            format!("{}({})", function, args.join(", "))
1785                        }
1786                    }
1787                    // DECY-130: malloc(size) → vec![0; count] or Vec::with_capacity()
1788                    // DECY-140: When target is raw pointer (*mut u8), use Box::leak for allocation
1789                    // DECY-142: When target is Vec<T>, generate vec with correct element type
1790                    // Reference: K&R §B5, ISO C99 §7.20.3.3
1791                    "malloc" => {
1792                        if arguments.len() == 1 {
1793                            let size_code =
1794                                self.generate_expression_with_context(&arguments[0], ctx);
1795
1796                            // DECY-142: Check if target is Vec<T> - generate vec with correct element type
1797                            if let Some(HirType::Vec(elem_type)) = target_type {
1798                                let elem_type_str = Self::map_type(elem_type);
1799                                let default_val = Self::default_value_for_type(elem_type);
1800                                // Try to detect n * sizeof(T) pattern for count
1801                                if let HirExpression::BinaryOp {
1802                                    op: BinaryOperator::Multiply,
1803                                    left,
1804                                    ..
1805                                } = &arguments[0]
1806                                {
1807                                    let count_code =
1808                                        self.generate_expression_with_context(left, ctx);
1809                                    // DECY-170: Wrap count expression in parens for correct precedence
1810                                    return format!(
1811                                        "vec![{}; ({}) as usize]",
1812                                        default_val, count_code
1813                                    );
1814                                } else {
1815                                    // DECY-170: Wrap size expression in parens for correct 'as' precedence
1816                                    // x + 1 as usize → x + (1 as usize) WRONG
1817                                    // (x + 1) as usize → correct
1818                                    return format!(
1819                                        "Vec::<{}>::with_capacity(({}) as usize)",
1820                                        elem_type_str, size_code
1821                                    );
1822                                }
1823                            }
1824
1825                            // DECY-140: Check if target is raw pointer - generate raw allocation
1826                            if let Some(HirType::Pointer(inner)) = target_type {
1827                                // malloc assigned to *mut T → Box::leak allocation
1828                                // This keeps the memory alive (leaked) so the raw pointer remains valid
1829                                if matches!(inner.as_ref(), HirType::Char) {
1830                                    // For char* / *mut u8: allocate byte buffer
1831                                    // DECY-170: Wrap size in parens for correct precedence
1832                                    return format!(
1833                                        "Box::leak(vec![0u8; ({}) as usize].into_boxed_slice()).as_mut_ptr()",
1834                                        size_code
1835                                    );
1836                                }
1837                                // DECY-160: For struct pointers like *mut Node: use Box::into_raw(Box::default())
1838                                // This allocates a default-initialized struct and returns a raw pointer to it
1839                                if let HirType::Struct(struct_name) = inner.as_ref() {
1840                                    return format!(
1841                                        "Box::into_raw(Box::<{}>::default())",
1842                                        struct_name
1843                                    );
1844                                }
1845                            }
1846
1847                            // Try to detect n * sizeof(T) pattern
1848                            if let HirExpression::BinaryOp {
1849                                op: BinaryOperator::Multiply,
1850                                left,
1851                                ..
1852                            } = &arguments[0]
1853                            {
1854                                // malloc(n * sizeof(T)) → vec![0i32; n]
1855                                let count_code = self.generate_expression_with_context(left, ctx);
1856                                // DECY-170: Wrap in parens for correct precedence
1857                                format!("vec![0i32; ({}) as usize]", count_code)
1858                            } else {
1859                                // malloc(size) → Vec::with_capacity(size)
1860                                // DECY-170: Wrap in parens for correct precedence
1861                                format!("Vec::<u8>::with_capacity(({}) as usize)", size_code)
1862                            }
1863                        } else {
1864                            "Vec::new()".to_string()
1865                        }
1866                    }
1867                    // DECY-130: calloc(count, size) → vec![0; count]
1868                    // Reference: K&R §B5, ISO C99 §7.20.3.1
1869                    "calloc" => {
1870                        if arguments.len() == 2 {
1871                            let count_code =
1872                                self.generate_expression_with_context(&arguments[0], ctx);
1873                            format!("vec![0i32; {} as usize]", count_code)
1874                        } else {
1875                            "Vec::new()".to_string()
1876                        }
1877                    }
1878                    // DECY-171: realloc(ptr, size) → realloc(ptr as *mut (), size) as *mut T
1879                    // Reference: K&R §B5, ISO C99 §7.20.3.4
1880                    // realloc takes void* and returns void*, so we need to:
1881                    // 1. Cast the typed pointer argument to *mut ()
1882                    // 2. Cast the return value to the target pointer type
1883                    "realloc" => {
1884                        if arguments.len() == 2 {
1885                            let ptr_code =
1886                                self.generate_expression_with_context(&arguments[0], ctx);
1887                            let size_code =
1888                                self.generate_expression_with_context(&arguments[1], ctx);
1889                            // Cast argument to *mut () for realloc
1890                            let realloc_call =
1891                                format!("realloc({} as *mut (), {})", ptr_code, size_code);
1892
1893                            // Cast return value to target type if known
1894                            if let Some(HirType::Pointer(inner)) = target_type {
1895                                let target_ptr_type =
1896                                    Self::map_type(&HirType::Pointer(inner.clone()));
1897                                format!("{} as {}", realloc_call, target_ptr_type)
1898                            } else {
1899                                realloc_call
1900                            }
1901                        } else {
1902                            "std::ptr::null_mut()".to_string()
1903                        }
1904                    }
1905                    // DECY-130: free(ptr) → drop(ptr) or comment (RAII handles it)
1906                    // Reference: K&R §B5, ISO C99 §7.20.3.2
1907                    "free" => {
1908                        if arguments.len() == 1 {
1909                            let ptr_code =
1910                                self.generate_expression_with_context(&arguments[0], ctx);
1911                            format!("drop({})", ptr_code)
1912                        } else {
1913                            "/* free() */".to_string()
1914                        }
1915                    }
1916                    // DECY-132: fopen(filename, mode) → std::fs::File::open/create
1917                    // Reference: K&R §7.5, ISO C99 §7.19.5.3
1918                    "fopen" => {
1919                        if arguments.len() == 2 {
1920                            let filename =
1921                                self.generate_expression_with_context(&arguments[0], ctx);
1922                            let mode = self.generate_expression_with_context(&arguments[1], ctx);
1923                            // Check mode: "r" → open, "w" → create
1924                            if mode.contains('w') || mode.contains('a') {
1925                                format!("std::fs::File::create({}).ok()", filename)
1926                            } else {
1927                                format!("std::fs::File::open({}).ok()", filename)
1928                            }
1929                        } else {
1930                            "None /* fopen requires 2 args */".to_string()
1931                        }
1932                    }
1933                    // DECY-132: fclose(f) → drop(f) (RAII handles cleanup)
1934                    // Reference: K&R §7.5, ISO C99 §7.19.5.1
1935                    "fclose" => {
1936                        if arguments.len() == 1 {
1937                            let file_code =
1938                                self.generate_expression_with_context(&arguments[0], ctx);
1939                            format!("drop({})", file_code)
1940                        } else {
1941                            "/* fclose() */".to_string()
1942                        }
1943                    }
1944                    // DECY-132: fgetc(f) → f.bytes().next().unwrap_or(Err(...))
1945                    // Reference: K&R §7.5, ISO C99 §7.19.7.1
1946                    "fgetc" | "getc" => {
1947                        if arguments.len() == 1 {
1948                            let file_code =
1949                                self.generate_expression_with_context(&arguments[0], ctx);
1950                            format!(
1951                                "{{ use std::io::Read; let mut buf = [0u8; 1]; {}.read(&mut buf).map(|_| buf[0] as i32).unwrap_or(-1) }}",
1952                                file_code
1953                            )
1954                        } else {
1955                            "-1 /* fgetc requires 1 arg */".to_string()
1956                        }
1957                    }
1958                    // DECY-132: fputc(c, f) → f.write(&[c as u8])
1959                    // Reference: K&R §7.5, ISO C99 §7.19.7.3
1960                    "fputc" | "putc" => {
1961                        if arguments.len() == 2 {
1962                            let char_code =
1963                                self.generate_expression_with_context(&arguments[0], ctx);
1964                            let file_code =
1965                                self.generate_expression_with_context(&arguments[1], ctx);
1966                            format!(
1967                                "{{ use std::io::Write; {}.write(&[{} as u8]).map(|_| {} as i32).unwrap_or(-1) }}",
1968                                file_code, char_code, char_code
1969                            )
1970                        } else {
1971                            "-1 /* fputc requires 2 args */".to_string()
1972                        }
1973                    }
1974                    // DECY-132: fprintf(f, fmt, ...) → write!(f, fmt, ...)
1975                    // Reference: K&R §7.2, ISO C99 §7.19.6.1
1976                    "fprintf" => {
1977                        if arguments.len() >= 2 {
1978                            let file_code =
1979                                self.generate_expression_with_context(&arguments[0], ctx);
1980                            let fmt = self.generate_expression_with_context(&arguments[1], ctx);
1981                            if arguments.len() == 2 {
1982                                format!(
1983                                    "{{ use std::io::Write; write!({}, {}).map(|_| 0).unwrap_or(-1) }}",
1984                                    file_code, fmt
1985                                )
1986                            } else {
1987                                // Has format args - simplified version
1988                                let args: Vec<String> = arguments[2..]
1989                                    .iter()
1990                                    .map(|a| self.generate_expression_with_context(a, ctx))
1991                                    .collect();
1992                                format!(
1993                                    "{{ use std::io::Write; write!({}, {}, {}).map(|_| 0).unwrap_or(-1) }}",
1994                                    file_code, fmt, args.join(", ")
1995                                )
1996                            }
1997                        } else {
1998                            "-1 /* fprintf requires 2+ args */".to_string()
1999                        }
2000                    }
2001                    // DECY-132: printf(fmt, ...) → print! macro
2002                    // Reference: K&R §7.2, ISO C99 §7.19.6.3
2003                    // DECY-119: Convert C format specifiers to Rust
2004                    // DECY-187: Wrap char* arguments with CStr for %s
2005                    "printf" => {
2006                        if !arguments.is_empty() {
2007                            let fmt = self.generate_expression_with_context(&arguments[0], ctx);
2008                            // Convert C format specifiers to Rust
2009                            let rust_fmt = Self::convert_c_format_to_rust(&fmt);
2010                            if arguments.len() == 1 {
2011                                format!("print!({})", rust_fmt)
2012                            } else {
2013                                // DECY-187: Find %s positions and wrap corresponding args with CStr
2014                                let s_positions = Self::find_string_format_positions(&fmt);
2015                                let args: Vec<String> = arguments[1..]
2016                                    .iter()
2017                                    .enumerate()
2018                                    .map(|(i, a)| {
2019                                        let arg_code = self.generate_expression_with_context(a, ctx);
2020                                        // If this arg corresponds to a %s, wrap with CStr
2021                                        // DECY-192: Skip wrapping for ternary expressions with string literals
2022                                        if s_positions.contains(&i) && !Self::is_string_ternary(a) {
2023                                            Self::wrap_with_cstr(&arg_code)
2024                                        } else {
2025                                            arg_code
2026                                        }
2027                                    })
2028                                    .collect();
2029                                format!("print!({}, {})", rust_fmt, args.join(", "))
2030                            }
2031                        } else {
2032                            "print!(\"\")".to_string()
2033                        }
2034                    }
2035                    // DECY-090: fread(buf, size, count, file) → file.read(&mut buf)
2036                    // Reference: K&R §7.5, ISO C99 §7.19.8.1
2037                    "fread" => {
2038                        if arguments.len() == 4 {
2039                            let buf_code =
2040                                self.generate_expression_with_context(&arguments[0], ctx);
2041                            let file_code =
2042                                self.generate_expression_with_context(&arguments[3], ctx);
2043                            format!(
2044                                "{{ use std::io::Read; {}.read(&mut {}).unwrap_or(0) }}",
2045                                file_code, buf_code
2046                            )
2047                        } else {
2048                            "0 /* fread requires 4 args */".to_string()
2049                        }
2050                    }
2051                    // DECY-090: fwrite(data, size, count, file) → file.write(&data)
2052                    // Reference: K&R §7.5, ISO C99 §7.19.8.2
2053                    "fwrite" => {
2054                        if arguments.len() == 4 {
2055                            let data_code =
2056                                self.generate_expression_with_context(&arguments[0], ctx);
2057                            let file_code =
2058                                self.generate_expression_with_context(&arguments[3], ctx);
2059                            format!(
2060                                "{{ use std::io::Write; {}.write(&{}).unwrap_or(0) }}",
2061                                file_code, data_code
2062                            )
2063                        } else {
2064                            "0 /* fwrite requires 4 args */".to_string()
2065                        }
2066                    }
2067                    // DECY-090: fputs(str, file) → file.write_all(str.as_bytes())
2068                    // Reference: K&R §7.5, ISO C99 §7.19.7.4
2069                    "fputs" => {
2070                        if arguments.len() == 2 {
2071                            let str_code =
2072                                self.generate_expression_with_context(&arguments[0], ctx);
2073                            let file_code =
2074                                self.generate_expression_with_context(&arguments[1], ctx);
2075                            format!(
2076                                "{{ use std::io::Write; {}.write_all({}.as_bytes()).map(|_| 0).unwrap_or(-1) }}",
2077                                file_code, str_code
2078                            )
2079                        } else {
2080                            "-1 /* fputs requires 2 args */".to_string()
2081                        }
2082                    }
2083                    // DECY-093: fork() → Command usage (no direct equivalent, skip)
2084                    "fork" => "/* fork() transformed to Command API */ 0".to_string(),
2085                    // DECY-093: execl/execlp → Command::new().status()
2086                    "execl" | "execlp" | "execle" | "execv" | "execvp" | "execve" => {
2087                        if !arguments.is_empty() {
2088                            let cmd = self.generate_expression_with_context(&arguments[0], ctx);
2089                            // Skip argv[0] (program name repeated), collect remaining args before NULL
2090                            let args: Vec<String> = arguments
2091                                .iter()
2092                                .skip(2) // Skip cmd path and argv[0]
2093                                .filter(|a| !matches!(a, HirExpression::NullLiteral))
2094                                .map(|a| self.generate_expression_with_context(a, ctx))
2095                                .collect();
2096                            if args.is_empty() {
2097                                format!(
2098                                    "{{ use std::process::Command; Command::new({}).status().expect(\"command failed\"); }}",
2099                                    cmd
2100                                )
2101                            } else {
2102                                let arg_chain: String =
2103                                    args.iter().map(|a| format!(".arg({})", a)).collect();
2104                                format!(
2105                                    "{{ use std::process::Command; Command::new({}){}.status().expect(\"command failed\"); }}",
2106                                    cmd, arg_chain
2107                                )
2108                            }
2109                        } else {
2110                            "/* exec requires args */".to_string()
2111                        }
2112                    }
2113                    // DECY-093: waitpid → .wait() (generated alongside spawn)
2114                    "waitpid" | "wait3" | "wait4" => {
2115                        "/* waitpid handled by Command API */ child.wait().expect(\"wait failed\")"
2116                            .to_string()
2117                    }
2118                    // DECY-094: wait(&status) → child.wait()
2119                    "wait" => "child.wait().expect(\"wait failed\")".to_string(),
2120                    // DECY-094: WEXITSTATUS(status) → status.code().unwrap_or(-1)
2121                    "WEXITSTATUS" => {
2122                        if !arguments.is_empty() {
2123                            let status_var =
2124                                self.generate_expression_with_context(&arguments[0], ctx);
2125                            format!("{}.code().unwrap_or(-1)", status_var)
2126                        } else {
2127                            "/* WEXITSTATUS requires status arg */".to_string()
2128                        }
2129                    }
2130                    // DECY-094: WIFEXITED(status) → status.success()
2131                    "WIFEXITED" => {
2132                        if !arguments.is_empty() {
2133                            let status_var =
2134                                self.generate_expression_with_context(&arguments[0], ctx);
2135                            format!("{}.success()", status_var)
2136                        } else {
2137                            "/* WIFEXITED requires status arg */".to_string()
2138                        }
2139                    }
2140                    // DECY-094: WIFSIGNALED(status) → status.signal().is_some()
2141                    "WIFSIGNALED" => {
2142                        if !arguments.is_empty() {
2143                            let status_var =
2144                                self.generate_expression_with_context(&arguments[0], ctx);
2145                            format!("{}.signal().is_some()", status_var)
2146                        } else {
2147                            "/* WIFSIGNALED requires status arg */".to_string()
2148                        }
2149                    }
2150                    // DECY-094: WTERMSIG(status) → status.signal().unwrap_or(0)
2151                    "WTERMSIG" => {
2152                        if !arguments.is_empty() {
2153                            let status_var =
2154                                self.generate_expression_with_context(&arguments[0], ctx);
2155                            format!("{}.signal().unwrap_or(0)", status_var)
2156                        } else {
2157                            "/* WTERMSIG requires status arg */".to_string()
2158                        }
2159                    }
2160                    // Default: pass through function call as-is
2161                    // DECY-116 + DECY-117: Transform call sites for slice functions and reference mutability
2162                    _ => {
2163                        // DECY-116: Check if this function has slice params (with removed length args)
2164                        let slice_mappings = ctx.get_slice_func_len_indices(function);
2165                        let len_indices_to_skip: std::collections::HashSet<usize> = slice_mappings
2166                            .map(|mappings| mappings.iter().map(|(_, len_idx)| *len_idx).collect())
2167                            .unwrap_or_default();
2168                        let array_indices: std::collections::HashSet<usize> = slice_mappings
2169                            .map(|mappings| mappings.iter().map(|(arr_idx, _)| *arr_idx).collect())
2170                            .unwrap_or_default();
2171
2172                        let args: Vec<String> = arguments
2173                            .iter()
2174                            .enumerate()
2175                            .filter_map(|(i, arg)| {
2176                                // DECY-116: Skip length arguments that were removed
2177                                if len_indices_to_skip.contains(&i) {
2178                                    return None;
2179                                }
2180
2181                                // DECY-116: Convert array args to slice references
2182                                if array_indices.contains(&i) {
2183                                    let arg_code = self.generate_expression_with_context(arg, ctx);
2184                                    return Some(format!("&{}", arg_code));
2185                                }
2186
2187                                // DECY-117: If arg is AddressOf (via UnaryOp or direct) and param expects &mut, generate &mut
2188                                let is_address_of = matches!(arg, HirExpression::AddressOf(_))
2189                                    || matches!(
2190                                        arg,
2191                                        HirExpression::UnaryOp {
2192                                            op: decy_hir::UnaryOperator::AddressOf,
2193                                            ..
2194                                        }
2195                                    );
2196
2197                                if is_address_of {
2198                                    // Extract the inner expression
2199                                    let inner = match arg {
2200                                        HirExpression::AddressOf(inner) => inner.as_ref(),
2201                                        HirExpression::UnaryOp { operand, .. } => operand.as_ref(),
2202                                        _ => unreachable!(),
2203                                    };
2204
2205                                    // Check if the function expects &mut for this parameter
2206                                    let expects_mut = ctx
2207                                        .get_function_param_type(function, i)
2208                                        .map(|t| {
2209                                            matches!(t, HirType::Reference { mutable: true, .. })
2210                                        })
2211                                        .unwrap_or(true); // Default to &mut for safety
2212
2213                                    let inner_code =
2214                                        self.generate_expression_with_context(inner, ctx);
2215                                    if expects_mut {
2216                                        Some(format!("&mut {}", inner_code))
2217                                    } else {
2218                                        Some(format!("&{}", inner_code))
2219                                    }
2220                                } else {
2221                                    // DECY-134b: Check if this function has string iteration params
2222                                    if let Some(string_iter_params) =
2223                                        ctx.get_string_iter_func(function)
2224                                    {
2225                                        // Check if this argument index is a string iteration param
2226                                        if let Some((_, is_mutable)) =
2227                                            string_iter_params.iter().find(|(idx, _)| *idx == i)
2228                                        {
2229                                            // Transform argument to slice reference
2230                                            // array → &mut array or &array
2231                                            // string literal → b"string" (byte slice)
2232                                            if let HirExpression::Variable(var_name) = arg {
2233                                                let var_type = ctx.get_type(var_name);
2234                                                if matches!(var_type, Some(HirType::Array { .. })) {
2235                                                    if *is_mutable {
2236                                                        return Some(format!("&mut {}", var_name));
2237                                                    } else {
2238                                                        return Some(format!("&{}", var_name));
2239                                                    }
2240                                                }
2241                                            }
2242                                            if let HirExpression::StringLiteral(s) = arg {
2243                                                // String literal becomes byte slice reference
2244                                                return Some(format!("b\"{}\"", s));
2245                                            }
2246                                            // AddressOf expressions (e.g., &buffer) - extract inner
2247                                            if let HirExpression::AddressOf(inner) = arg {
2248                                                let inner_code = self
2249                                                    .generate_expression_with_context(inner, ctx);
2250                                                if *is_mutable {
2251                                                    return Some(format!("&mut {}", inner_code));
2252                                                } else {
2253                                                    return Some(format!("&{}", inner_code));
2254                                                }
2255                                            }
2256                                        }
2257                                    }
2258
2259                                    // DECY-125: Check if param is raw pointer and arg needs conversion
2260                                    let param_type = ctx.get_function_param_type(function, i);
2261                                    let is_raw_pointer_param = param_type
2262                                        .map(|t| matches!(t, HirType::Pointer(_)))
2263                                        .unwrap_or(false);
2264
2265                                    if is_raw_pointer_param {
2266                                        // Convert array/variable to .as_mut_ptr()
2267                                        if let HirExpression::Variable(var_name) = arg {
2268                                            // Check if it's an array type in context
2269                                            let var_type = ctx.get_type(var_name);
2270                                            if matches!(var_type, Some(HirType::Array { .. })) {
2271                                                return Some(format!("{}.as_mut_ptr()", var_name));
2272                                            }
2273                                        }
2274                                        // Convert string literal to .as_ptr() as *mut u8
2275                                        if let HirExpression::StringLiteral(s) = arg {
2276                                            return Some(format!("\"{}\".as_ptr() as *mut u8", s));
2277                                        }
2278                                    }
2279
2280                                    // DECY-123: Check if param expects &mut but arg is raw pointer
2281                                    let is_ref_param = param_type
2282                                        .map(|t| matches!(t, HirType::Reference { .. }))
2283                                        .unwrap_or(false);
2284                                    if is_ref_param {
2285                                        if let HirExpression::Variable(var_name) = arg {
2286                                            let var_type = ctx.get_type(var_name);
2287                                            // Raw pointer to reference: unsafe { &mut *ptr }
2288                                            if matches!(var_type, Some(HirType::Pointer(_))) {
2289                                                // DECY-143: Add SAFETY comment
2290                                                return Some(Self::unsafe_block(
2291                                                    &format!("&mut *{}", var_name),
2292                                                    "pointer is non-null and valid for the duration of the call",
2293                                                ));
2294                                            }
2295                                        }
2296                                    }
2297
2298                                    // DECY-197: Check if param is unsized array (slice param) and arg is sized array
2299                                    // C's `void func(char arr[])` becomes `fn func(arr: &mut [u8])` in Rust
2300                                    // When calling with fixed-size array, add `&mut` prefix
2301                                    let is_slice_param = param_type
2302                                        .map(|t| matches!(t, HirType::Array { size: None, .. }))
2303                                        .unwrap_or(false);
2304                                    if is_slice_param {
2305                                        if let HirExpression::Variable(var_name) = arg {
2306                                            let var_type = ctx.get_type(var_name);
2307                                            // Fixed-size array to slice: add &mut prefix
2308                                            if matches!(var_type, Some(HirType::Array { size: Some(_), .. })) {
2309                                                return Some(format!("&mut {}", var_name));
2310                                            }
2311                                        }
2312                                    }
2313
2314                                    // DECY-199: Check if param expects Int but arg is CharLiteral
2315                                    // putchar(' ') needs ' ' as i32, not b' '
2316                                    let is_int_param = param_type
2317                                        .map(|t| matches!(t, HirType::Int))
2318                                        .unwrap_or(false);
2319                                    if is_int_param {
2320                                        if let HirExpression::CharLiteral(c) = arg {
2321                                            // Cast char to i32
2322                                            return Some(format!("{}i32", *c as i32));
2323                                        }
2324                                    }
2325
2326                                    // DECY-140: Check if param expects &str but arg is a raw pointer field
2327                                    // This happens when calling strcmp/strncmp with entry->key where key is char*
2328                                    // For stdlib string functions, params are &str but we might pass *mut u8 field
2329                                    let is_string_param = param_type
2330                                        .map(|t| matches!(t, HirType::StringReference | HirType::StringLiteral))
2331                                        .unwrap_or(false);
2332                                    // Also check for known stdlib string functions that expect &str
2333                                    let is_string_func = matches!(
2334                                        function.as_str(),
2335                                        "strcmp" | "strncmp" | "strchr" | "strrchr" | "strstr" | "strlen"
2336                                    );
2337                                    if is_string_param || is_string_func {
2338                                        // Check if arg is PointerFieldAccess (entry->key pattern)
2339                                        if let HirExpression::PointerFieldAccess { pointer, field } = arg {
2340                                            // Generate CStr conversion for null-terminated C string
2341                                            // DECY-143: Add SAFETY comment
2342                                            let ptr_code = self.generate_expression_with_context(pointer, ctx);
2343                                            return Some(Self::unsafe_block(
2344                                                &format!("std::ffi::CStr::from_ptr((*{}).{} as *const i8).to_str().unwrap_or(\"\")", ptr_code, field),
2345                                                "string pointer is null-terminated and valid",
2346                                            ));
2347                                        }
2348                                    }
2349
2350                                    Some(self.generate_expression_with_context(arg, ctx))
2351                                }
2352                            })
2353                            .collect();
2354                        format!("{}({})", function, args.join(", "))
2355                    }
2356                }
2357            }
2358            HirExpression::FieldAccess { object, field } => {
2359                format!(
2360                    "{}.{}",
2361                    self.generate_expression_with_context(object, ctx),
2362                    field
2363                )
2364            }
2365            HirExpression::PointerFieldAccess { pointer, field } => {
2366                // In Rust, ptr->field becomes (*ptr).field
2367                // However, if the pointer is already a field access (ptr->field1->field2),
2368                // we should generate (*ptr).field1.field2 not (*(*ptr).field1).field2
2369                match &**pointer {
2370                    // If the pointer is itself a field access expression, we can chain with .
2371                    HirExpression::PointerFieldAccess { .. }
2372                    | HirExpression::FieldAccess { .. } => {
2373                        format!(
2374                            "{}.{}",
2375                            self.generate_expression_with_context(pointer, ctx),
2376                            field
2377                        )
2378                    }
2379                    // For other expressions (variables, array index, etc), we need explicit deref
2380                    _ => {
2381                        let ptr_code = self.generate_expression_with_context(pointer, ctx);
2382                        // DECY-129: Check if pointer is a raw pointer - if so, wrap in unsafe
2383                        if let HirExpression::Variable(var_name) = &**pointer {
2384                            if ctx.is_pointer(var_name) {
2385                                // DECY-143: Add SAFETY comment
2386                                return Self::unsafe_block(
2387                                    &format!("(*{}).{}", ptr_code, field),
2388                                    "pointer is non-null and points to valid struct",
2389                                );
2390                            }
2391                        }
2392                        format!("(*{}).{}", ptr_code, field)
2393                    }
2394                }
2395            }
2396            HirExpression::ArrayIndex { array, index } => {
2397                let array_code = self.generate_expression_with_context(array, ctx);
2398                let index_code = self.generate_expression_with_context(index, ctx);
2399
2400                // DECY-041: Check if array is a raw pointer - if so, use unsafe pointer arithmetic
2401                // DECY-165: Also check via infer_expression_type for struct field access
2402                let is_raw_pointer = if let HirExpression::Variable(var_name) = &**array {
2403                    ctx.is_pointer(var_name)
2404                } else {
2405                    // Use type inference for complex expressions like sb->data
2406                    matches!(ctx.infer_expression_type(array), Some(HirType::Pointer(_)))
2407                };
2408
2409                if is_raw_pointer {
2410                    // Raw pointer indexing: arr[i] becomes unsafe { *arr.add(i as usize) }
2411                    // DECY-143: Add SAFETY comment
2412                    return Self::unsafe_block(
2413                        &format!("*{}.add(({}) as usize)", array_code, index_code),
2414                        "index is within bounds of allocated array",
2415                    );
2416                }
2417
2418                // Regular array/slice indexing
2419                // DECY-072: Cast index to usize for slice indexing
2420                // DECY-150: Wrap index in parens to handle operator precedence
2421                // e.g., buffer[size - 1 - i as usize] parses as buffer[size - 1 - (i as usize)]
2422                // but buffer[(size - 1 - i) as usize] casts entire expression correctly
2423                format!("{}[({}) as usize]", array_code, index_code)
2424            }
2425            HirExpression::SliceIndex { slice, index, .. } => {
2426                // DECY-070 GREEN: Generate safe slice indexing (0 unsafe blocks!)
2427                // SliceIndex represents pointer arithmetic transformed to safe indexing
2428                let slice_code = self.generate_expression_with_context(slice, ctx);
2429                let index_code = self.generate_expression_with_context(index, ctx);
2430                // DECY-113: Generate: slice[index as usize] - cast i32 index to usize
2431                // Slice indexing requires usize, but C typically uses int (i32)
2432                // DECY-150: Wrap index in parens to handle operator precedence
2433                format!("{}[({}) as usize]", slice_code, index_code)
2434            }
2435            HirExpression::Sizeof { type_name } => {
2436                // sizeof(int) → std::mem::size_of::<i32>() as i32
2437                // sizeof(struct Data) → std::mem::size_of::<Data>() as i32
2438                // Note: size_of returns usize, but C's sizeof returns int (typically i32)
2439
2440                // DECY-189: Detect sizeof(expr) that was mis-parsed as sizeof(type)
2441                // Pattern: "record name" came from sizeof(record->name) where
2442                // the parser tokenized record and name as separate identifiers
2443                let trimmed = type_name.trim();
2444                let is_member_access = trimmed.contains(' ')
2445                    && !trimmed.starts_with("struct ")
2446                    && !trimmed.starts_with("unsigned ")
2447                    && !trimmed.starts_with("signed ")
2448                    && !trimmed.starts_with("long ")
2449                    && !trimmed.starts_with("short ");
2450
2451                if is_member_access {
2452                    // DECY-189: sizeof(record->field) → std::mem::size_of_val(&(*record).field)
2453                    // Split "record field" into parts and reconstruct member access
2454                    let parts: Vec<&str> = trimmed.split_whitespace().collect();
2455                    if parts.len() >= 2 {
2456                        let var = parts[0];
2457                        let field = parts[1..].join(".");
2458                        format!("std::mem::size_of_val(&(*{}).{}) as i32", var, field)
2459                    } else {
2460                        // Fallback: shouldn't happen, but be safe
2461                        let rust_type = self.map_sizeof_type(type_name);
2462                        format!("std::mem::size_of::<{}>() as i32", rust_type)
2463                    }
2464                } else {
2465                    // DECY-205: Check if this is sizeof(variable) not sizeof(type)
2466                    // If the type_name is a known variable, use size_of_val instead
2467                    if ctx.get_type(trimmed).is_some() {
2468                        // It's a variable - use size_of_val
2469                        format!("std::mem::size_of_val(&{}) as i32", trimmed)
2470                    } else {
2471                        let rust_type = self.map_sizeof_type(type_name);
2472                        format!("std::mem::size_of::<{}>() as i32", rust_type)
2473                    }
2474                }
2475            }
2476            HirExpression::NullLiteral => {
2477                // NULL → None
2478                "None".to_string()
2479            }
2480            HirExpression::IsNotNull(inner) => {
2481                // p != NULL → if let Some(p) = p
2482                // This is a helper expression for generating Option checks
2483                // In actual codegen, we transform if (p) to if let Some(_) = p
2484                let inner_code = self.generate_expression_with_context(inner, ctx);
2485                format!("if let Some(_) = {}", inner_code)
2486            }
2487            HirExpression::Calloc {
2488                count,
2489                element_type,
2490            } => {
2491                // calloc(n, sizeof(T)) → vec![0T; n]
2492                // Generate zero-initialized vec![default; count]
2493                let count_code = self.generate_expression_with_context(count, ctx);
2494
2495                // Get default value with type suffix for clarity
2496                let default_value = match element_type.as_ref() {
2497                    HirType::Int => "0i32",
2498                    HirType::UnsignedInt => "0u32", // DECY-158
2499                    HirType::Float => "0.0f32",
2500                    HirType::Double => "0.0f64",
2501                    HirType::Char => "0u8",
2502                    _ => &Self::default_value_for_type(element_type),
2503                };
2504
2505                format!("vec![{}; {}]", default_value, count_code)
2506            }
2507            HirExpression::Malloc { size } => {
2508                // malloc(size) should have been transformed to Box or Vec by analyzer
2509                // If we're generating this directly, treat it as Box::new(default)
2510                // Note: The proper transformation should happen at HIR level via PatternDetector
2511
2512                // Try to detect if this is an array allocation (n * sizeof(T))
2513                // If so, generate Vec::with_capacity
2514                if let HirExpression::BinaryOp {
2515                    op: decy_hir::BinaryOperator::Multiply,
2516                    left,
2517                    ..
2518                } = size.as_ref()
2519                {
2520                    // This looks like n * sizeof(T) → Vec::with_capacity(n)
2521                    let capacity_code = self.generate_expression_with_context(left, ctx);
2522                    format!("Vec::with_capacity({})", capacity_code)
2523                } else {
2524                    // Single allocation → Box::new(default)
2525                    "Box::new(0i32)".to_string()
2526                }
2527            }
2528            HirExpression::Realloc { pointer, new_size } => {
2529                // realloc(ptr, new_size) transformation depends on context:
2530                // 1. realloc(NULL, size) → treat as malloc (Vec allocation)
2531                // 2. realloc(ptr, 0) → treat as free (RAII comment or clear)
2532                // 3. realloc(ptr, new_size) → vec.resize(new_count, default)
2533                //
2534                // Since we're generating an expression here, we'll return a placeholder
2535                // The actual transformation should happen in Assignment statement handling
2536                // For now, just generate a comment indicating this needs special handling
2537
2538                // Check if pointer is NULL → malloc equivalent
2539                if matches!(**pointer, HirExpression::NullLiteral) {
2540                    // realloc(NULL, size) → vec![default; count]
2541                    if let HirExpression::BinaryOp {
2542                        op: decy_hir::BinaryOperator::Multiply,
2543                        left,
2544                        ..
2545                    } = new_size.as_ref()
2546                    {
2547                        let count_code = self.generate_expression_with_context(left, ctx);
2548                        format!("vec![0i32; {}]", count_code)
2549                    } else {
2550                        "Vec::new()".to_string()
2551                    }
2552                } else {
2553                    // realloc(ptr, size) - this should be handled at statement level
2554                    // For expression context, return the pointer unchanged as a placeholder
2555                    self.generate_expression_with_context(pointer, ctx)
2556                }
2557            }
2558            HirExpression::StringMethodCall {
2559                receiver,
2560                method,
2561                arguments,
2562            } => {
2563                let receiver_code = self.generate_expression_with_context(receiver, ctx);
2564                if arguments.is_empty() {
2565                    // DECY-072: Cast .len() to i32 for slices/arrays (used with i32 loop counters)
2566                    // String .len() returns usize which is typically what's needed for strings
2567                    // But array/slice .len() often needs i32 for C loop patterns
2568                    // We cast for all cases to maintain C semantics (where sizeof returns int)
2569                    if method == "len" {
2570                        format!("{}.{}() as i32", receiver_code, method)
2571                    } else {
2572                        format!("{}.{}()", receiver_code, method)
2573                    }
2574                } else {
2575                    let args: Vec<String> = arguments
2576                        .iter()
2577                        .map(|arg| {
2578                            // For clone_into, we need &mut on the destination
2579                            if method == "clone_into" {
2580                                format!("&mut {}", self.generate_expression_with_context(arg, ctx))
2581                            } else {
2582                                self.generate_expression_with_context(arg, ctx)
2583                            }
2584                        })
2585                        .collect();
2586                    format!("{}.{}({})", receiver_code, method, args.join(", "))
2587                }
2588            }
2589            HirExpression::Cast { target_type: cast_target, expr } => {
2590                // DECY-220: When outer target is Vec<T> and inner is malloc/calloc,
2591                // unwrap the cast and generate malloc with Vec target type directly.
2592                // This handles: int* arr = (int*)malloc(n * sizeof(int)) → let arr: Vec<i32> = vec![...];
2593                if let Some(vec_type @ HirType::Vec(_)) = target_type {
2594                    if Self::is_any_malloc_or_calloc(expr) {
2595                        // Skip the cast, generate the inner malloc with Vec target type
2596                        return self.generate_expression_with_target_type(expr, ctx, Some(vec_type));
2597                    }
2598                }
2599
2600                // C: (int)x → Rust: x as i32
2601                // Sprint 19 Feature (DECY-059)
2602                let expr_code = self.generate_expression_with_context(expr, ctx);
2603                let rust_type = Self::map_type(cast_target);
2604
2605                // Wrap in parentheses if the expression is a binary operation
2606                let expr_str = if matches!(**expr, HirExpression::BinaryOp { .. }) {
2607                    format!("({})", expr_code)
2608                } else {
2609                    expr_code.clone()
2610                };
2611
2612                // DECY-208: Handle pointer-to-integer casts
2613                // C: (long)&x → Rust: &x as *const i32 as isize
2614                // References can't be cast directly to integers in Rust
2615                let is_address_of = matches!(**expr, HirExpression::AddressOf(_))
2616                    || matches!(
2617                        &**expr,
2618                        HirExpression::UnaryOp {
2619                            op: decy_hir::UnaryOperator::AddressOf,
2620                            ..
2621                        }
2622                    );
2623
2624                let is_integer_target = matches!(
2625                    cast_target,
2626                    HirType::Int | HirType::UnsignedInt | HirType::Char
2627                );
2628
2629                if is_address_of && is_integer_target {
2630                    // Cast reference to raw pointer first, then to isize, then to target type
2631                    // References can't be cast directly to integers in Rust
2632                    format!("{} as *const _ as isize as {}", expr_str, rust_type)
2633                } else {
2634                    format!("{} as {}", expr_str, rust_type)
2635                }
2636            }
2637            HirExpression::CompoundLiteral {
2638                literal_type,
2639                initializers,
2640            } => {
2641                // C: (struct Point){10, 20} → Rust: Point { x: 10, y: 20 }
2642                // C: (int[]){1, 2, 3} → Rust: vec![1, 2, 3] or [1, 2, 3]
2643                // Sprint 19 Feature (DECY-060)
2644                // DECY-133: Use actual field names from struct definition
2645                match literal_type {
2646                    HirType::Struct(name) => {
2647                        // Generate struct literal: StructName { x: val0, y: val1, ..Default::default() }
2648                        if initializers.is_empty() {
2649                            // Empty struct: Point {}
2650                            format!("{} {{}}", name)
2651                        } else {
2652                            // DECY-133: Look up struct field names from context
2653                            let struct_fields = ctx.structs.get(name);
2654                            let num_struct_fields = struct_fields.map(|f| f.len()).unwrap_or(0);
2655
2656                            let fields: Vec<String> = initializers
2657                                .iter()
2658                                .enumerate()
2659                                .map(|(i, init)| {
2660                                    let init_code =
2661                                        self.generate_expression_with_context(init, ctx);
2662                                    // Use actual field name if available, fallback to field{i}
2663                                    let field_name = struct_fields
2664                                        .and_then(|f| f.get(i))
2665                                        .map(|(name, _)| name.as_str())
2666                                        .unwrap_or_else(|| {
2667                                            Box::leak(format!("field{}", i).into_boxed_str())
2668                                        });
2669                                    format!("{}: {}", field_name, init_code)
2670                                })
2671                                .collect();
2672
2673                            // DECY-133: Add ..Default::default() if not all fields are initialized
2674                            // This handles designated initializers that skip fields
2675                            if initializers.len() < num_struct_fields {
2676                                format!(
2677                                    "{} {{ {}, ..Default::default() }}",
2678                                    name,
2679                                    fields.join(", ")
2680                                )
2681                            } else {
2682                                format!("{} {{ {} }}", name, fields.join(", "))
2683                            }
2684                        }
2685                    }
2686                    HirType::Array { .. } => {
2687                        // DECY-199: Generate array literal [1, 2, 3] instead of vec![...]
2688                        // Fixed-size arrays should use array literals, not Vec
2689                        if initializers.is_empty() {
2690                            "[]".to_string()
2691                        } else {
2692                            let elements: Vec<String> = initializers
2693                                .iter()
2694                                .map(|init| self.generate_expression_with_context(init, ctx))
2695                                .collect();
2696                            format!("[{}]", elements.join(", "))
2697                        }
2698                    }
2699                    _ => {
2700                        // For other types, generate a reasonable default
2701                        // This is a simplified implementation
2702                        format!(
2703                            "/* Compound literal of type {} */",
2704                            Self::map_type(literal_type)
2705                        )
2706                    }
2707                }
2708            }
2709            // DECY-139: Post-increment expression (x++)
2710            // C semantics: returns old value, then increments
2711            // Rust: { let __tmp = x; x += 1; __tmp }
2712            HirExpression::PostIncrement { operand } => {
2713                let operand_code = self.generate_expression_with_context(operand, ctx);
2714
2715                // DECY-138: Special handling for &str post-increment (string iteration)
2716                // C pattern: *key++ where key is const char*
2717                // Rust pattern: { let __tmp = key.as_bytes()[0] as u32; key = &key[1..]; __tmp }
2718                // DECY-158: Cast to u32 for C-compatible unsigned promotion semantics
2719                // In C, char is promoted to int/unsigned int in arithmetic - u32 works for both
2720                if let HirExpression::Variable(var_name) = &**operand {
2721                    if let Some(var_type) = ctx.get_type(var_name) {
2722                        if matches!(var_type, HirType::StringReference | HirType::StringLiteral) {
2723                            return format!(
2724                                "{{ let __tmp = {var}.as_bytes()[0] as u32; {var} = &{var}[1..]; __tmp }}",
2725                                var = operand_code
2726                            );
2727                        }
2728                    }
2729                }
2730
2731                format!(
2732                    "{{ let __tmp = {operand}; {operand} += 1; __tmp }}",
2733                    operand = operand_code
2734                )
2735            }
2736            // DECY-139: Pre-increment expression (++x)
2737            // C semantics: increments first, then returns new value
2738            // Rust: { x += 1; x }
2739            HirExpression::PreIncrement { operand } => {
2740                let operand_code = self.generate_expression_with_context(operand, ctx);
2741                format!("{{ {operand} += 1; {operand} }}", operand = operand_code)
2742            }
2743            // DECY-139: Post-decrement expression (x--)
2744            // C semantics: returns old value, then decrements
2745            // Rust: { let __tmp = x; x -= 1; __tmp }
2746            HirExpression::PostDecrement { operand } => {
2747                let operand_code = self.generate_expression_with_context(operand, ctx);
2748                format!(
2749                    "{{ let __tmp = {operand}; {operand} -= 1; __tmp }}",
2750                    operand = operand_code
2751                )
2752            }
2753            // DECY-139: Pre-decrement expression (--x)
2754            // C semantics: decrements first, then returns new value
2755            // Rust: { x -= 1; x }
2756            HirExpression::PreDecrement { operand } => {
2757                let operand_code = self.generate_expression_with_context(operand, ctx);
2758                format!("{{ {operand} -= 1; {operand} }}", operand = operand_code)
2759            }
2760            // DECY-192: Ternary/Conditional expression (cond ? then : else)
2761            // C: (a > b) ? a : b → Rust: if a > b { a } else { b }
2762            // DECY-213: Propagate target_type to branches for proper string literal conversion
2763            HirExpression::Ternary {
2764                condition,
2765                then_expr,
2766                else_expr,
2767            } => {
2768                let cond_code = self.generate_expression_with_context(condition, ctx);
2769                // Propagate target type to both branches for proper type coercion
2770                // This allows string literals in ternary to become *mut u8 when needed
2771                let then_code = self.generate_expression_with_target_type(then_expr, ctx, target_type);
2772                let else_code = self.generate_expression_with_target_type(else_expr, ctx, target_type);
2773
2774                // Convert condition to boolean if it's not already
2775                let cond_bool = if Self::is_boolean_expression(condition) {
2776                    cond_code
2777                } else {
2778                    format!("{} != 0", cond_code)
2779                };
2780
2781                format!("if {} {{ {} }} else {{ {} }}", cond_bool, then_code, else_code)
2782            }
2783        }
2784    }
2785
2786    /// Convert unary operator to string.
2787    fn unary_operator_to_string(op: &decy_hir::UnaryOperator) -> &'static str {
2788        use decy_hir::UnaryOperator;
2789        match op {
2790            UnaryOperator::Minus => "-",
2791            UnaryOperator::LogicalNot => "!",
2792            // DECY-193: In Rust, bitwise NOT is ! (same as logical NOT for bool)
2793            UnaryOperator::BitwiseNot => "!",
2794            UnaryOperator::AddressOf => "&",
2795            // Post/Pre-increment/decrement are handled as block expressions
2796            // in generate_expression_with_context, so should never reach here
2797            UnaryOperator::PostIncrement
2798            | UnaryOperator::PostDecrement
2799            | UnaryOperator::PreIncrement
2800            | UnaryOperator::PreDecrement => {
2801                unreachable!("Increment/decrement operators should be handled as block expressions")
2802            }
2803        }
2804    }
2805
2806    /// Check if an expression already produces a boolean result.
2807    /// Used to avoid redundant `!= 0` conversions for expressions that are already boolean.
2808    fn is_boolean_expression(expr: &HirExpression) -> bool {
2809        match expr {
2810            // Comparison operators always produce bool
2811            HirExpression::BinaryOp { op, .. } => matches!(
2812                op,
2813                BinaryOperator::Equal
2814                    | BinaryOperator::NotEqual
2815                    | BinaryOperator::LessThan
2816                    | BinaryOperator::GreaterThan
2817                    | BinaryOperator::LessEqual
2818                    | BinaryOperator::GreaterEqual
2819                    | BinaryOperator::LogicalAnd
2820                    | BinaryOperator::LogicalOr
2821            ),
2822            // Logical NOT produces bool
2823            HirExpression::UnaryOp {
2824                op: decy_hir::UnaryOperator::LogicalNot,
2825                ..
2826            } => true,
2827            // Other expressions are assumed to be non-boolean (integers, etc.)
2828            _ => false,
2829        }
2830    }
2831
2832    /// DECY-138: Check if expression is a dereference of a string variable.
2833    /// Returns the variable name if it's a string dereference, None otherwise.
2834    /// Used for string iteration pattern: while (*str) → while !str.is_empty()
2835    fn get_string_deref_var(expr: &HirExpression, ctx: &TypeContext) -> Option<String> {
2836        match expr {
2837            // Direct dereference: *str
2838            HirExpression::Dereference(inner) => {
2839                if let HirExpression::Variable(var_name) = &**inner {
2840                    if let Some(var_type) = ctx.get_type(var_name) {
2841                        if matches!(var_type, HirType::StringReference | HirType::StringLiteral) {
2842                            return Some(var_name.clone());
2843                        }
2844                    }
2845                }
2846                None
2847            }
2848            // Comparison with 0: *str != 0 or *str == 0
2849            HirExpression::BinaryOp { op, left, right } => {
2850                if matches!(op, BinaryOperator::NotEqual | BinaryOperator::Equal) {
2851                    // Check: *str != 0 or *str == 0
2852                    if let HirExpression::IntLiteral(0) = &**right {
2853                        return Self::get_string_deref_var(left, ctx);
2854                    }
2855                    if let HirExpression::IntLiteral(0) = &**left {
2856                        return Self::get_string_deref_var(right, ctx);
2857                    }
2858                }
2859                None
2860            }
2861            _ => None,
2862        }
2863    }
2864
2865    /// Convert binary operator to string.
2866    fn binary_operator_to_string(op: &BinaryOperator) -> &'static str {
2867        match op {
2868            BinaryOperator::Add => "+",
2869            BinaryOperator::Subtract => "-",
2870            BinaryOperator::Multiply => "*",
2871            BinaryOperator::Divide => "/",
2872            BinaryOperator::Modulo => "%",
2873            BinaryOperator::Equal => "==",
2874            BinaryOperator::NotEqual => "!=",
2875            BinaryOperator::LessThan => "<",
2876            BinaryOperator::GreaterThan => ">",
2877            BinaryOperator::LessEqual => "<=",
2878            BinaryOperator::GreaterEqual => ">=",
2879            BinaryOperator::LogicalAnd => "&&",
2880            BinaryOperator::LogicalOr => "||",
2881            // DECY-137: Bitwise and shift operators
2882            BinaryOperator::LeftShift => "<<",
2883            BinaryOperator::RightShift => ">>",
2884            BinaryOperator::BitwiseAnd => "&",
2885            BinaryOperator::BitwiseOr => "|",
2886            BinaryOperator::BitwiseXor => "^",
2887            // DECY-195: Assignment operator (for embedded assignments)
2888            BinaryOperator::Assign => "=",
2889        }
2890    }
2891
2892    /// Get default value for a type (for uninitialized variables).
2893    fn default_value_for_type(hir_type: &HirType) -> String {
2894        match hir_type {
2895            HirType::Void => "()".to_string(),
2896            HirType::Int => "0i32".to_string(),
2897            HirType::UnsignedInt => "0u32".to_string(), // DECY-158
2898            HirType::Float => "0.0f32".to_string(),
2899            HirType::Double => "0.0f64".to_string(),
2900            HirType::Char => "0u8".to_string(),
2901            HirType::Pointer(_) => "std::ptr::null_mut()".to_string(),
2902            HirType::Box(inner) => {
2903                // Box types should not use default values, they should be initialized with Box::new
2904                // This is just a fallback
2905                format!("Box::new({})", Self::default_value_for_type(inner))
2906            }
2907            HirType::Vec(_) => {
2908                // Vec types default to empty Vec
2909                "Vec::new()".to_string()
2910            }
2911            HirType::Option(_) => {
2912                // Option types default to None
2913                "None".to_string()
2914            }
2915            HirType::Reference { .. } => {
2916                // References cannot have default values - they must always be initialized
2917                // This should never be reached in valid code
2918                panic!("References must be initialized and cannot have default values")
2919            }
2920            HirType::Struct(name) => {
2921                // Use ::default() for struct initialization
2922                // For structs with large arrays (>32 elements), see DECY-123 special handling
2923                // in variable declaration codegen where struct definitions are available
2924                format!("{}::default()", name)
2925            }
2926            HirType::Enum(name) => {
2927                format!("{}::default()", name)
2928            }
2929            HirType::Array { element_type, size } => {
2930                if let Some(n) = size {
2931                    format!("[{}; {}]", Self::default_value_for_type(element_type), n)
2932                } else {
2933                    // Unsized arrays cannot have default values - this should be initialized from parameter
2934                    panic!("Unsized arrays must be initialized and cannot have default values")
2935                }
2936            }
2937            HirType::FunctionPointer { .. } => {
2938                // DECY-202: Function pointers default to None when wrapped in Option
2939                // For local variables, return None which will be unwrapped if needed
2940                "None".to_string()
2941            }
2942            HirType::StringLiteral => {
2943                // String literals default to empty string slice
2944                r#""""#.to_string()
2945            }
2946            HirType::OwnedString => {
2947                // Owned strings default to String::new()
2948                "String::new()".to_string()
2949            }
2950            HirType::StringReference => {
2951                // String references default to empty string slice
2952                r#""""#.to_string()
2953            }
2954            HirType::Union(_) => {
2955                // Unions will be transformed to enums
2956                // Default to the first variant's default value
2957                panic!("Union types must be initialized and cannot have default values")
2958            }
2959            // DECY-172: Type aliases use 0 as default (for size_t/ssize_t/ptrdiff_t)
2960            HirType::TypeAlias(name) => {
2961                // size_t/ssize_t/ptrdiff_t default to 0
2962                match name.as_str() {
2963                    "size_t" => "0usize".to_string(),
2964                    "ssize_t" | "ptrdiff_t" => "0isize".to_string(),
2965                    _ => "0".to_string(),
2966                }
2967            }
2968        }
2969    }
2970
2971    /// Convert C printf format specifiers to Rust format specifiers.
2972    /// DECY-119: %d → {}, %s → {}, %f → {}, etc.
2973    fn convert_c_format_to_rust(c_fmt: &str) -> String {
2974        // If it's a quoted string literal, process the contents
2975        let trimmed = c_fmt.trim();
2976        if trimmed.starts_with('"') && trimmed.ends_with('"') && trimmed.len() >= 2 {
2977            let inner = &trimmed[1..trimmed.len() - 1];
2978            let converted = Self::convert_format_specifiers(inner);
2979            format!("\"{}\"", converted)
2980        } else {
2981            // Not a string literal, return as-is
2982            c_fmt.to_string()
2983        }
2984    }
2985
2986    /// DECY-193: Convert C format specifiers to Rust format specifiers.
2987    /// Handles width, precision, and flags like %02X, %10.3f, etc.
2988    fn convert_format_specifiers(input: &str) -> String {
2989        let mut result = String::new();
2990        let chars: Vec<char> = input.chars().collect();
2991        let mut i = 0;
2992
2993        while i < chars.len() {
2994            if chars[i] == '%' {
2995                if i + 1 < chars.len() && chars[i + 1] == '%' {
2996                    // %% -> %
2997                    result.push('%');
2998                    i += 2;
2999                    continue;
3000                }
3001
3002                // Parse format specifier: %[flags][width][.precision][length]specifier
3003                let start = i;
3004                i += 1; // skip %
3005
3006                let mut flags = String::new();
3007                let mut width = String::new();
3008                let mut precision = String::new();
3009
3010                // Parse flags: -, +, space, #, 0
3011                while i < chars.len() && "-+ #0".contains(chars[i]) {
3012                    if chars[i] == '0' {
3013                        flags.push('0'); // zero-padding
3014                    }
3015                    // Skip other flags for now (- is left-align, + is sign, etc.)
3016                    i += 1;
3017                }
3018
3019                // Parse width
3020                while i < chars.len() && chars[i].is_ascii_digit() {
3021                    width.push(chars[i]);
3022                    i += 1;
3023                }
3024
3025                // Parse precision
3026                if i < chars.len() && chars[i] == '.' {
3027                    i += 1;
3028                    while i < chars.len() && chars[i].is_ascii_digit() {
3029                        precision.push(chars[i]);
3030                        i += 1;
3031                    }
3032                }
3033
3034                // Skip length modifiers: h, hh, l, ll, L, z, j, t
3035                while i < chars.len() && "hlLzjt".contains(chars[i]) {
3036                    i += 1;
3037                }
3038
3039                // Parse specifier
3040                if i < chars.len() {
3041                    let spec = chars[i];
3042                    i += 1;
3043
3044                    // Build Rust format specifier
3045                    let rust_spec = match spec {
3046                        'd' | 'i' | 'u' => {
3047                            if !width.is_empty() || !flags.is_empty() {
3048                                format!("{{:{}{}}}", flags, width)
3049                            } else {
3050                                "{}".to_string()
3051                            }
3052                        }
3053                        'x' => {
3054                            if !width.is_empty() || !flags.is_empty() {
3055                                format!("{{:{}{}x}}", flags, width)
3056                            } else {
3057                                "{:x}".to_string()
3058                            }
3059                        }
3060                        'X' => {
3061                            if !width.is_empty() || !flags.is_empty() {
3062                                format!("{{:{}{}X}}", flags, width)
3063                            } else {
3064                                "{:X}".to_string()
3065                            }
3066                        }
3067                        'o' => {
3068                            if !width.is_empty() || !flags.is_empty() {
3069                                format!("{{:{}{}o}}", flags, width)
3070                            } else {
3071                                "{:o}".to_string()
3072                            }
3073                        }
3074                        'f' | 'F' => {
3075                            if !precision.is_empty() {
3076                                if !width.is_empty() {
3077                                    format!("{{:{}{}.{}}}", flags, width, precision)
3078                                } else {
3079                                    format!("{{:.{}}}", precision)
3080                                }
3081                            } else if !width.is_empty() {
3082                                format!("{{:{}{}}}", flags, width)
3083                            } else {
3084                                "{}".to_string()
3085                            }
3086                        }
3087                        'e' => "{:e}".to_string(),
3088                        'E' => "{:E}".to_string(),
3089                        'g' | 'G' => "{}".to_string(),
3090                        's' => {
3091                            if !width.is_empty() {
3092                                format!("{{:{}}}", width)
3093                            } else {
3094                                "{}".to_string()
3095                            }
3096                        }
3097                        'c' => "{}".to_string(),
3098                        'p' => "{:p}".to_string(),
3099                        _ => {
3100                            // Unknown specifier, keep original
3101                            input[start..i].to_string()
3102                        }
3103                    };
3104                    result.push_str(&rust_spec);
3105                } else {
3106                    // Incomplete format specifier at end of string
3107                    result.push_str(&input[start..]);
3108                }
3109            } else {
3110                result.push(chars[i]);
3111                i += 1;
3112            }
3113        }
3114
3115        result
3116    }
3117
3118    /// DECY-187: Find positions of %s format specifiers in a format string.
3119    /// Returns 0-indexed positions corresponding to printf arguments.
3120    fn find_string_format_positions(fmt: &str) -> Vec<usize> {
3121        let mut positions = Vec::new();
3122        let mut arg_index = 0;
3123        let trimmed = fmt.trim();
3124
3125        // Extract inner content if quoted
3126        let inner = if trimmed.starts_with('"') && trimmed.ends_with('"') && trimmed.len() >= 2 {
3127            &trimmed[1..trimmed.len() - 1]
3128        } else {
3129            trimmed
3130        };
3131
3132        let chars: Vec<char> = inner.chars().collect();
3133        let mut i = 0;
3134        while i < chars.len() {
3135            if chars[i] == '%' && i + 1 < chars.len() {
3136                let next = chars[i + 1];
3137                // Skip %% (literal percent)
3138                if next == '%' {
3139                    i += 2;
3140                    continue;
3141                }
3142                // Check for format specifiers
3143                // Skip width/precision modifiers and length specifiers
3144                let mut j = i + 1;
3145                while j < chars.len() && (chars[j].is_ascii_digit() || chars[j] == '.' || chars[j] == '-' || chars[j] == '+' || chars[j] == ' ' || chars[j] == '#' || chars[j] == '*') {
3146                    j += 1;
3147                }
3148                // Skip length modifiers (l, ll, h, hh, z, etc.)
3149                while j < chars.len() && (chars[j] == 'l' || chars[j] == 'h' || chars[j] == 'z' || chars[j] == 'j' || chars[j] == 't' || chars[j] == 'L') {
3150                    j += 1;
3151                }
3152                // Now we should be at the conversion specifier
3153                if j < chars.len() {
3154                    let specifier = chars[j];
3155                    if specifier == 's' {
3156                        positions.push(arg_index);
3157                    }
3158                    // Count this as an argument position (for d, i, u, f, s, c, p, x, X, o, e, E, g, G, n)
3159                    if specifier == 'd' || specifier == 'i' || specifier == 'u' || specifier == 'f' ||
3160                       specifier == 's' || specifier == 'c' || specifier == 'p' || specifier == 'x' ||
3161                       specifier == 'X' || specifier == 'o' || specifier == 'e' || specifier == 'E' ||
3162                       specifier == 'g' || specifier == 'G' || specifier == 'n' || specifier == 'a' ||
3163                       specifier == 'A' {
3164                        arg_index += 1;
3165                    }
3166                    i = j + 1;
3167                } else {
3168                    i += 1;
3169                }
3170            } else {
3171                i += 1;
3172            }
3173        }
3174        positions
3175    }
3176
3177    /// DECY-187: Wrap a char* argument with CStr conversion for safe printing.
3178    /// DECY-199: Use .as_ptr() for arrays which can't be cast directly to pointers.
3179    fn wrap_with_cstr(arg: &str) -> String {
3180        format!(
3181            "unsafe {{ std::ffi::CStr::from_ptr({}.as_ptr() as *const i8).to_str().unwrap_or(\"\") }}",
3182            arg
3183        )
3184    }
3185
3186    /// DECY-192: Check if expression is a ternary that returns string literals.
3187    /// Such expressions should not be wrapped with CStr since they return &str directly in Rust.
3188    fn is_string_ternary(expr: &HirExpression) -> bool {
3189        if let HirExpression::Ternary {
3190            then_expr,
3191            else_expr,
3192            ..
3193        } = expr
3194        {
3195            matches!(**then_expr, HirExpression::StringLiteral(_))
3196                && matches!(**else_expr, HirExpression::StringLiteral(_))
3197        } else {
3198            false
3199        }
3200    }
3201
3202    /// Generate code for a statement.
3203    pub fn generate_statement(&self, stmt: &HirStatement) -> String {
3204        self.generate_statement_for_function(stmt, None)
3205    }
3206
3207    /// Generate code for a statement, with optional function context.
3208    ///
3209    /// When function_name is "main", special handling applies (DECY-AUDIT-001):
3210    /// - return N; becomes std::process::exit(N);
3211    fn generate_statement_for_function(
3212        &self,
3213        stmt: &HirStatement,
3214        function_name: Option<&str>,
3215    ) -> String {
3216        self.generate_statement_with_context(stmt, function_name, &mut TypeContext::new(), None)
3217    }
3218
3219    /// Generate code for a statement with type context for pointer arithmetic and return type for null pointer detection.
3220    fn generate_statement_with_context(
3221        &self,
3222        stmt: &HirStatement,
3223        function_name: Option<&str>,
3224        ctx: &mut TypeContext,
3225        return_type: Option<&HirType>,
3226    ) -> String {
3227        match stmt {
3228            HirStatement::VariableDeclaration {
3229                name,
3230                var_type,
3231                initializer,
3232            } => {
3233                // Check for VLA pattern: Array with size: None and an initializer
3234                // C99 VLA: int arr[n]; where n is runtime-determined
3235                // Rust: let arr = vec![0i32; n];
3236                if let HirType::Array {
3237                    element_type,
3238                    size: None,
3239                } = var_type
3240                {
3241                    // This is a VLA - transform to Vec
3242                    if let Some(size_expr) = initializer {
3243                        // VLA → Vec
3244                        let size_code = self.generate_expression_with_context(size_expr, ctx);
3245                        let default_value = match element_type.as_ref() {
3246                            HirType::Int => "0i32",
3247                            HirType::UnsignedInt => "0u32", // DECY-158
3248                            HirType::Float => "0.0f32",
3249                            HirType::Double => "0.0f64",
3250                            HirType::Char => "0u8",
3251                            _ => &Self::default_value_for_type(element_type),
3252                        };
3253
3254                        // Register the variable as Vec type in context
3255                        ctx.add_variable(
3256                            name.clone(),
3257                            HirType::Vec(Box::new(element_type.as_ref().clone())),
3258                        );
3259
3260                        return format!(
3261                            "let mut {} = vec![{}; {}];",
3262                            name, default_value, size_code
3263                        );
3264                    }
3265                }
3266
3267                // DECY-118: DISABLED for local variables due to double pointer issues (DECY-128)
3268                // When a local pointer's address is taken (e.g., &p in set_value(&p, 42)),
3269                // transforming `int *p = &x` to `&mut x` breaks type compatibility.
3270                // The transformation works for parameters (which are passed by value),
3271                // but not for locals where &p may be needed.
3272                // See DECY-128 for tracking re-enabling this with address-taken analysis.
3273                // Original transform: int *p = &x;  →  Rust: let p = &mut x;
3274
3275                // DECY-130: Check if this is a malloc/calloc initialization
3276                // If so, change the type from *mut T to Vec<T>
3277                // DECY-220: Use helper that checks through Cast expressions
3278                let is_malloc_init = if let Some(init_expr) = initializer {
3279                    Self::is_any_malloc_or_calloc(init_expr)
3280                } else {
3281                    false
3282                };
3283
3284                // Adjust type for malloc initialization:
3285                // - Struct pointer → Box<T> (single allocation)
3286                // - Primitive pointer with array pattern (n * sizeof) → Vec<T>
3287                // - Other → Vec<T> (default for dynamic allocation)
3288                let (_actual_type, type_str) = if is_malloc_init {
3289                    if let HirType::Pointer(inner) = var_type {
3290                        // Check if this is a struct allocation (should use Box)
3291                        // vs array allocation (should use Vec)
3292                        let is_struct_alloc = matches!(&**inner, HirType::Struct(_));
3293
3294                        // Also check if the malloc argument is NOT an array pattern (n * sizeof)
3295                        let is_array_pattern = if let Some(init_expr) = initializer {
3296                            match init_expr {
3297                                HirExpression::FunctionCall {
3298                                    function,
3299                                    arguments,
3300                                } if function == "malloc" || function == "calloc" => arguments
3301                                    .first()
3302                                    .map(|arg| {
3303                                        matches!(
3304                                            arg,
3305                                            HirExpression::BinaryOp {
3306                                                op: decy_hir::BinaryOperator::Multiply,
3307                                                ..
3308                                            }
3309                                        )
3310                                    })
3311                                    .unwrap_or(false),
3312                                _ => false,
3313                            }
3314                        } else {
3315                            false
3316                        };
3317
3318                        if is_struct_alloc && !is_array_pattern {
3319                            // Single struct allocation → Box<T>
3320                            let box_type = HirType::Box(inner.clone());
3321                            ctx.add_variable(name.clone(), box_type.clone());
3322                            (box_type.clone(), Self::map_type(&box_type))
3323                        } else {
3324                            // Array allocation or primitive → Vec<T>
3325                            let vec_type = HirType::Vec(inner.clone());
3326                            ctx.add_variable(name.clone(), vec_type.clone());
3327                            (vec_type.clone(), Self::map_type(&vec_type))
3328                        }
3329                    } else {
3330                        ctx.add_variable(name.clone(), var_type.clone());
3331                        (var_type.clone(), Self::map_type(var_type))
3332                    }
3333                } else {
3334                    // DECY-088: Check for char* with string literal initializer → &str
3335                    let is_string_literal_init =
3336                        matches!(initializer, Some(HirExpression::StringLiteral(_)));
3337                    let is_char_pointer = matches!(
3338                        var_type,
3339                        HirType::Pointer(inner) if matches!(&**inner, HirType::Char)
3340                    );
3341
3342                    if is_char_pointer && is_string_literal_init {
3343                        // char* s = "hello" → let s: &str = "hello"
3344                        ctx.add_variable(name.clone(), HirType::StringReference);
3345                        (HirType::StringReference, "&str".to_string())
3346                    } else {
3347                        ctx.add_variable(name.clone(), var_type.clone());
3348                        (var_type.clone(), Self::map_type(var_type))
3349                    }
3350                };
3351
3352                // DECY-088: For string literals, use immutable binding
3353                let mutability = if matches!(_actual_type, HirType::StringReference) {
3354                    ""
3355                } else {
3356                    "mut "
3357                };
3358                let mut code = format!("let {}{}: {}", mutability, name, type_str);
3359                if let Some(init_expr) = initializer {
3360                    // Special handling for Malloc expressions - use var_type to generate correct Box::new
3361                    if matches!(init_expr, HirExpression::Malloc { .. }) {
3362                        // Malloc → Box::new or Vec (depending on var_type)
3363                        match var_type {
3364                            HirType::Box(inner) => {
3365                                code.push_str(&format!(
3366                                    " = Box::new({});",
3367                                    Self::default_value_for_type(inner)
3368                                ));
3369                            }
3370                            HirType::Vec(_) => {
3371                                // Extract capacity from malloc size expression
3372                                if let HirExpression::Malloc { size } = init_expr {
3373                                    if let HirExpression::BinaryOp {
3374                                        op: decy_hir::BinaryOperator::Multiply,
3375                                        left,
3376                                        ..
3377                                    } = size.as_ref()
3378                                    {
3379                                        let capacity_code =
3380                                            self.generate_expression_with_context(left, ctx);
3381                                        code.push_str(&format!(
3382                                            " = Vec::with_capacity({});",
3383                                            capacity_code
3384                                        ));
3385                                    } else {
3386                                        code.push_str(" = Vec::new();");
3387                                    }
3388                                } else {
3389                                    code.push_str(" = Vec::new();");
3390                                }
3391                            }
3392                            _ => {
3393                                // Default to Box::new(0i32) for other types
3394                                code.push_str(" = Box::new(0i32);");
3395                            }
3396                        }
3397                    } else if is_malloc_init {
3398                        // Handle FunctionCall { function: "malloc" } for struct allocations
3399                        // Generate Box::new or Vec based on the MODIFIED type (_actual_type)
3400                        match &_actual_type {
3401                            HirType::Box(inner) => {
3402                                // DECY-141: Use Box::default() for safe zero-initialization
3403                                // if the inner type is a struct that derives Default
3404                                let use_default =
3405                                    if let HirType::Struct(struct_name) = inner.as_ref() {
3406                                        ctx.struct_has_default(struct_name)
3407                                    } else {
3408                                        false
3409                                    };
3410
3411                                if use_default {
3412                                    // Safe: struct derives Default
3413                                    code.push_str(" = Box::default();");
3414                                } else {
3415                                    // Fallback: use zeroed for structs with large arrays or unknown types
3416                                    // DECY-143: Add SAFETY comment
3417                                    let inner_type = Self::map_type(inner);
3418                                    code.push_str(&format!(
3419                                        " = Box::new(/* SAFETY: {} is valid when zero-initialized */ unsafe {{ std::mem::zeroed::<{}>() }});",
3420                                        inner_type, inner_type
3421                                    ));
3422                                }
3423                            }
3424                            HirType::Vec(_) => {
3425                                // DECY-169: Pass the TRANSFORMED type (_actual_type), not the original
3426                                // pointer type (var_type). This ensures the expression generator
3427                                // produces Vec code to match the Vec type annotation.
3428                                code.push_str(&format!(
3429                                    " = {};",
3430                                    self.generate_expression_with_target_type(
3431                                        init_expr,
3432                                        ctx,
3433                                        Some(&_actual_type)
3434                                    )
3435                                ));
3436                            }
3437                            _ => {
3438                                // Fallback to expression generator
3439                                code.push_str(&format!(
3440                                    " = {};",
3441                                    self.generate_expression_with_target_type(
3442                                        init_expr,
3443                                        ctx,
3444                                        Some(var_type)
3445                                    )
3446                                ));
3447                            }
3448                        }
3449                    } else {
3450                        // DECY-199: Handle char array initialization from string literal
3451                        // char str[N] = "hello" → let mut str: [u8; N] = *b"hello\0"
3452                        let is_char_array = matches!(
3453                            var_type,
3454                            HirType::Array { element_type, .. }
3455                            if matches!(&**element_type, HirType::Char)
3456                        );
3457
3458                        if is_char_array {
3459                            if let HirExpression::StringLiteral(s) = init_expr {
3460                                // Generate byte string with null terminator, dereferenced to value
3461                                // The string from clang already has escape sequences like \n as literal
3462                                // characters (\, n). We just need to escape internal quotes.
3463                                // Escape sequences from C source are preserved as-is.
3464                                let escaped: String = s.chars().map(|c| {
3465                                    match c {
3466                                        '"' => "\\\"".to_string(),
3467                                        c => c.to_string(),
3468                                    }
3469                                }).collect();
3470                                code.push_str(&format!(" = *b\"{}\\0\";", escaped));
3471                            } else {
3472                                // Non-string initializer for char array
3473                                code.push_str(&format!(
3474                                    " = {};",
3475                                    self.generate_expression_with_target_type(
3476                                        init_expr,
3477                                        ctx,
3478                                        Some(var_type)
3479                                    )
3480                                ));
3481                            }
3482                        } else {
3483                            // DECY-214: Pass _actual_type (not var_type) for proper target typing
3484                            // When char* + string literal was detected, _actual_type is StringReference
3485                            // and we should NOT convert the string literal to *mut u8
3486                            code.push_str(&format!(
3487                                " = {};",
3488                                self.generate_expression_with_target_type(
3489                                    init_expr,
3490                                    ctx,
3491                                    Some(&_actual_type)
3492                                )
3493                            ));
3494                        }
3495                    }
3496                } else {
3497                    // Provide default value for uninitialized variables
3498                    code.push_str(&format!(" = {};", Self::default_value_for_type(var_type)));
3499                }
3500                code
3501            }
3502            HirStatement::Return(expr_opt) => {
3503                // Special handling for main function (DECY-AUDIT-001)
3504                // return N; in main becomes std::process::exit(N);
3505                if function_name == Some("main") {
3506                    if let Some(expr) = expr_opt {
3507                        let expr_code = self.generate_expression_with_context(expr, ctx);
3508                        // DECY-126: Check if expression type needs cast to i32
3509                        // exit() expects i32, but char/u8 expressions need casting
3510                        let expr_type = ctx.infer_expression_type(expr);
3511                        let needs_cast = matches!(expr_type, Some(HirType::Char));
3512                        if needs_cast {
3513                            format!("std::process::exit({} as i32);", expr_code)
3514                        } else {
3515                            format!("std::process::exit({});", expr_code)
3516                        }
3517                    } else {
3518                        "std::process::exit(0);".to_string()
3519                    }
3520                } else if let Some(expr) = expr_opt {
3521                    // Pass return type as target type hint for null pointer detection
3522                    format!(
3523                        "return {};",
3524                        self.generate_expression_with_target_type(expr, ctx, return_type)
3525                    )
3526                } else {
3527                    "return;".to_string()
3528                }
3529            }
3530            HirStatement::If {
3531                condition,
3532                then_block,
3533                else_block,
3534            } => {
3535                let mut code = String::new();
3536
3537                // Generate if condition
3538                // DECY-131: If condition is not already boolean, wrap with != 0
3539                let cond_code = self.generate_expression_with_context(condition, ctx);
3540                let cond_str = if Self::is_boolean_expression(condition) {
3541                    cond_code
3542                } else {
3543                    format!("({}) != 0", cond_code)
3544                };
3545                code.push_str(&format!("if {} {{\n", cond_str));
3546
3547                // Generate then block
3548                for stmt in then_block {
3549                    code.push_str("    ");
3550                    code.push_str(&self.generate_statement_with_context(
3551                        stmt,
3552                        function_name,
3553                        ctx,
3554                        return_type,
3555                    ));
3556                    code.push('\n');
3557                }
3558
3559                // Generate else block if present
3560                if let Some(else_stmts) = else_block {
3561                    code.push_str("} else {\n");
3562                    for stmt in else_stmts {
3563                        code.push_str("    ");
3564                        code.push_str(&self.generate_statement_with_context(
3565                            stmt,
3566                            function_name,
3567                            ctx,
3568                            return_type,
3569                        ));
3570                        code.push('\n');
3571                    }
3572                }
3573
3574                code.push('}');
3575                code
3576            }
3577            HirStatement::While { condition, body } => {
3578                let mut code = String::new();
3579
3580                // Generate while condition
3581                // DECY-138: Check for string iteration pattern: while (*str) → while !str.is_empty()
3582                let cond_str = if let Some(str_var) = Self::get_string_deref_var(condition, ctx) {
3583                    format!("!{}.is_empty()", str_var)
3584                } else {
3585                    // DECY-123: If condition is not already boolean, wrap with != 0
3586                    let cond_code = self.generate_expression_with_context(condition, ctx);
3587                    if Self::is_boolean_expression(condition) {
3588                        cond_code
3589                    } else {
3590                        format!("({}) != 0", cond_code)
3591                    }
3592                };
3593                code.push_str(&format!("while {} {{\n", cond_str));
3594
3595                // Generate loop body
3596                for stmt in body {
3597                    code.push_str("    ");
3598                    code.push_str(&self.generate_statement_with_context(
3599                        stmt,
3600                        function_name,
3601                        ctx,
3602                        return_type,
3603                    ));
3604                    code.push('\n');
3605                }
3606
3607                code.push('}');
3608                code
3609            }
3610            HirStatement::Break => "break;".to_string(),
3611            HirStatement::Continue => "continue;".to_string(),
3612            HirStatement::Assignment { target, value } => {
3613                // Special handling for realloc() → Vec::resize/truncate/clear
3614                if let HirExpression::Realloc { pointer, new_size } = value {
3615                    // target is a String (variable name) in Assignment statements
3616                    let target_var = target.clone();
3617
3618                    // Check if target is a Vec type to get element type
3619                    let element_type = if let Some(HirType::Vec(inner)) = ctx.get_type(&target_var)
3620                    {
3621                        inner.as_ref().clone()
3622                    } else {
3623                        // Fallback: assume i32
3624                        HirType::Int
3625                    };
3626
3627                    // Check special cases:
3628                    // 1. realloc(ptr, 0) → clear or RAII comment
3629                    if let HirExpression::IntLiteral(0) = **new_size {
3630                        return format!("{}.clear(); // Free equivalent: clear vector", target_var);
3631                    }
3632
3633                    // 2. realloc(NULL, size) → should not appear in assignment (would be in initializer)
3634                    //    but handle it gracefully if it does
3635                    if matches!(**pointer, HirExpression::NullLiteral) {
3636                        // This is essentially malloc - but in assignment context, we'll treat it as resize from 0
3637                        if let HirExpression::BinaryOp {
3638                            op: decy_hir::BinaryOperator::Multiply,
3639                            left,
3640                            ..
3641                        } = new_size.as_ref()
3642                        {
3643                            let count_code = self.generate_expression_with_context(left, ctx);
3644                            let default_value = Self::default_value_for_type(&element_type);
3645                            return format!(
3646                                "{}.resize({}, {})",
3647                                target_var, count_code, default_value
3648                            );
3649                        }
3650                    }
3651
3652                    // 3. realloc(ptr, new_size) → vec.resize(new_count, default)
3653                    // Extract count from new_size (typically n * sizeof(T))
3654                    if let HirExpression::BinaryOp {
3655                        op: decy_hir::BinaryOperator::Multiply,
3656                        left,
3657                        ..
3658                    } = new_size.as_ref()
3659                    {
3660                        let count_code = self.generate_expression_with_context(left, ctx);
3661                        let default_value = Self::default_value_for_type(&element_type);
3662                        format!("{}.resize({}, {});", target_var, count_code, default_value)
3663                    } else {
3664                        // Fallback: if new_size is not n * sizeof(T), generate direct resize
3665                        // This handles edge cases where size isn't n * sizeof(T)
3666                        let size_expr = self.generate_expression_with_context(new_size, ctx);
3667                        let default_value = Self::default_value_for_type(&element_type);
3668                        format!(
3669                            "{}.resize({} as usize, {});",
3670                            target_var, size_expr, default_value
3671                        )
3672                    }
3673                } else {
3674                    // DECY-134: Check for string iteration param pointer arithmetic
3675                    // ptr = ptr + 1 → ptr_idx += 1
3676                    if let Some(idx_var) = ctx.get_string_iter_index(target) {
3677                        // Check if this is ptr = ptr + N or ptr = ptr - N
3678                        if let HirExpression::BinaryOp { op, left, right } = value {
3679                            if let HirExpression::Variable(var_name) = &**left {
3680                                if var_name == target {
3681                                    let right_code =
3682                                        self.generate_expression_with_context(right, ctx);
3683                                    return match op {
3684                                        BinaryOperator::Add => {
3685                                            format!("{} += {} as usize;", idx_var, right_code)
3686                                        }
3687                                        BinaryOperator::Subtract => {
3688                                            format!("{} -= {} as usize;", idx_var, right_code)
3689                                        }
3690                                        _ => format!(
3691                                            "{} = {};",
3692                                            target,
3693                                            self.generate_expression_with_context(value, ctx)
3694                                        ),
3695                                    };
3696                                }
3697                            }
3698                        }
3699                    }
3700                    // Regular assignment (not realloc)
3701                    let target_type = ctx.get_type(target);
3702                    format!(
3703                        "{} = {};",
3704                        target,
3705                        self.generate_expression_with_target_type(value, ctx, target_type)
3706                    )
3707                }
3708            }
3709            HirStatement::For {
3710                init,
3711                condition,
3712                increment,
3713                body,
3714            } => {
3715                let mut code = String::new();
3716
3717                // Generate init statement before loop (if present)
3718                if let Some(init_stmt) = init {
3719                    code.push_str(&self.generate_statement_with_context(
3720                        init_stmt,
3721                        function_name,
3722                        ctx,
3723                        return_type,
3724                    ));
3725                    code.push('\n');
3726                }
3727
3728                // Generate while loop with condition
3729                code.push_str(&format!(
3730                    "while {} {{\n",
3731                    self.generate_expression_with_context(condition, ctx)
3732                ));
3733
3734                // Generate loop body
3735                for stmt in body {
3736                    code.push_str("    ");
3737                    code.push_str(&self.generate_statement_with_context(
3738                        stmt,
3739                        function_name,
3740                        ctx,
3741                        return_type,
3742                    ));
3743                    code.push('\n');
3744                }
3745
3746                // Generate increment at end of body (if present)
3747                if let Some(inc_stmt) = increment {
3748                    code.push_str("    ");
3749                    code.push_str(&self.generate_statement_with_context(
3750                        inc_stmt,
3751                        function_name,
3752                        ctx,
3753                        return_type,
3754                    ));
3755                    code.push('\n');
3756                }
3757
3758                code.push('}');
3759                code
3760            }
3761            HirStatement::Switch {
3762                condition,
3763                cases,
3764                default_case,
3765            } => {
3766                let mut code = String::new();
3767
3768                // Generate match expression
3769                code.push_str(&format!(
3770                    "match {} {{\n",
3771                    self.generate_expression_with_context(condition, ctx)
3772                ));
3773
3774                // DECY-209: Infer switch condition type for case pattern matching
3775                let condition_type = ctx.infer_expression_type(condition);
3776                let condition_is_int = matches!(condition_type, Some(HirType::Int));
3777
3778                // Generate each case
3779                for case in cases {
3780                    if let Some(value_expr) = &case.value {
3781                        // Generate case pattern
3782                        // DECY-209: If condition is Int and case is CharLiteral, cast to i32
3783                        let case_pattern = if condition_is_int
3784                            && matches!(value_expr, HirExpression::CharLiteral(_))
3785                        {
3786                            format!(
3787                                "{} as i32",
3788                                self.generate_expression_with_context(value_expr, ctx)
3789                            )
3790                        } else {
3791                            self.generate_expression_with_context(value_expr, ctx)
3792                        };
3793                        code.push_str(&format!("    {} => {{\n", case_pattern));
3794
3795                        // Generate case body (filter out Break statements)
3796                        for stmt in &case.body {
3797                            if !matches!(stmt, HirStatement::Break) {
3798                                code.push_str("        ");
3799                                code.push_str(&self.generate_statement_with_context(
3800                                    stmt,
3801                                    function_name,
3802                                    ctx,
3803                                    return_type,
3804                                ));
3805                                code.push('\n');
3806                            }
3807                        }
3808
3809                        code.push_str("    },\n");
3810                    }
3811                }
3812
3813                // Generate default case (or empty default if not present)
3814                code.push_str("    _ => {\n");
3815                if let Some(default_stmts) = default_case {
3816                    for stmt in default_stmts {
3817                        if !matches!(stmt, HirStatement::Break) {
3818                            code.push_str("        ");
3819                            code.push_str(&self.generate_statement_with_context(
3820                                stmt,
3821                                function_name,
3822                                ctx,
3823                                return_type,
3824                            ));
3825                            code.push('\n');
3826                        }
3827                    }
3828                }
3829                code.push_str("    },\n");
3830
3831                code.push('}');
3832                code
3833            }
3834            HirStatement::DerefAssignment { target, value } => {
3835                // DECY-185: Handle struct field access targets directly (no dereference needed)
3836                // sb->capacity = value should generate (*sb).capacity = value, not *(*sb).capacity = value
3837                if matches!(
3838                    target,
3839                    HirExpression::PointerFieldAccess { .. } | HirExpression::FieldAccess { .. }
3840                ) {
3841                    let target_code = self.generate_expression_with_context(target, ctx);
3842                    let value_code = self.generate_expression_with_context(value, ctx);
3843                    return format!("{} = {};", target_code, value_code);
3844                }
3845
3846                // DECY-134: Check for string iteration param - use slice indexing
3847                if let HirExpression::Variable(var_name) = target {
3848                    if let Some(idx_var) = ctx.get_string_iter_index(var_name) {
3849                        // Transform *ptr = value to slice[idx] = value - no unsafe needed!
3850                        let value_code = self.generate_expression_with_context(value, ctx);
3851                        return format!("{}[{}] = {};", var_name, idx_var, value_code);
3852                    }
3853                }
3854
3855                // Infer the type of *target for null pointer detection
3856                let target_type = ctx
3857                    .infer_expression_type(&HirExpression::Dereference(Box::new(target.clone())));
3858                let target_code = self.generate_expression_with_context(target, ctx);
3859                let value_code =
3860                    self.generate_expression_with_target_type(value, ctx, target_type.as_ref());
3861
3862                // Helper to strip nested unsafe blocks - returns owned String to avoid lifetime issues
3863                fn strip_unsafe(code: &str) -> String {
3864                    if code.starts_with("unsafe { ") && code.ends_with(" }") {
3865                        code.strip_prefix("unsafe { ")
3866                            .and_then(|s| s.strip_suffix(" }"))
3867                            .unwrap_or(code)
3868                            .to_string()
3869                    } else {
3870                        code.to_string()
3871                    }
3872                }
3873
3874                // DECY-124: Check if target is a raw pointer - if so, wrap in unsafe
3875                if let HirExpression::Variable(var_name) = target {
3876                    if ctx.is_pointer(var_name) {
3877                        // DECY-127: Strip nested unsafe from value_code to avoid warnings
3878                        let clean_value = strip_unsafe(&value_code);
3879                        // DECY-143: Add SAFETY comment
3880                        return Self::unsafe_stmt(
3881                            &format!("*{} = {}", target_code, clean_value),
3882                            "pointer is valid, aligned, and not aliased during write",
3883                        );
3884                    }
3885                }
3886
3887                // DECY-128: Check if target is Dereference(Variable) where variable holds a raw pointer
3888                // e.g., **ptr = val where ptr is &mut *mut T
3889                // *ptr yields *mut T (raw pointer), so **ptr needs unsafe
3890                if let HirExpression::Dereference(inner) = target {
3891                    if let HirExpression::Variable(var_name) = &**inner {
3892                        // Check if dereferencing yields a raw pointer
3893                        // This happens when var_type is Reference to Pointer or Pointer to Pointer
3894                        if let Some(var_type) = ctx.get_type(var_name) {
3895                            let yields_raw_ptr = match var_type {
3896                                HirType::Reference {
3897                                    inner: ref_inner, ..
3898                                } => {
3899                                    matches!(&**ref_inner, HirType::Pointer(_))
3900                                }
3901                                HirType::Pointer(ptr_inner) => {
3902                                    matches!(&**ptr_inner, HirType::Pointer(_))
3903                                }
3904                                _ => false,
3905                            };
3906                            if yields_raw_ptr {
3907                                let clean_value = strip_unsafe(&value_code);
3908                                // DECY-143: Add SAFETY comment
3909                                return Self::unsafe_stmt(
3910                                    &format!("*{} = {}", target_code, clean_value),
3911                                    "double pointer dereference - inner pointer is valid and writable",
3912                                );
3913                            }
3914                        }
3915                    }
3916                }
3917
3918                format!("*{} = {};", target_code, value_code)
3919            }
3920            HirStatement::ArrayIndexAssignment {
3921                array,
3922                index,
3923                value,
3924            } => {
3925                // Infer the type of array[index] for null pointer detection
3926                let target_expr = HirExpression::ArrayIndex {
3927                    array: array.clone(),
3928                    index: index.clone(),
3929                };
3930                let target_type = ctx.infer_expression_type(&target_expr);
3931
3932                // DECY-165: Check if array is a raw pointer - if so, use unsafe pointer arithmetic
3933                let is_raw_pointer = if let HirExpression::Variable(var_name) = &**array {
3934                    ctx.is_pointer(var_name)
3935                } else {
3936                    // Use type inference for complex expressions like sb->data
3937                    matches!(ctx.infer_expression_type(array), Some(HirType::Pointer(_)))
3938                };
3939
3940                let array_code = self.generate_expression_with_context(array, ctx);
3941                let index_code = self.generate_expression_with_context(index, ctx);
3942                let mut value_code =
3943                    self.generate_expression_with_target_type(value, ctx, target_type.as_ref());
3944
3945                // DECY-210: Handle int-to-char coercion for array element assignment
3946                // In C, s[i] = (n % 10) + '0' works because char is widened to int then truncated back
3947                // In Rust, we need explicit cast when assigning int to u8 element
3948                if matches!(target_type, Some(HirType::Char)) {
3949                    let value_type = ctx.infer_expression_type(value);
3950                    if matches!(value_type, Some(HirType::Int)) {
3951                        value_code = format!("({}) as u8", value_code);
3952                    }
3953                }
3954
3955                if is_raw_pointer {
3956                    // Raw pointer indexing: arr[i] = v becomes unsafe { *arr.add(i as usize) = v }
3957                    // DECY-143: Add SAFETY comment
3958                    Self::unsafe_stmt(
3959                        &format!(
3960                            "*{}.add(({}) as usize) = {}",
3961                            array_code, index_code, value_code
3962                        ),
3963                        "index is within bounds of allocated array",
3964                    )
3965                } else {
3966                    // DECY-072: Cast index to usize for slice indexing
3967                    // DECY-150: Wrap index in parens to handle operator precedence
3968                    format!(
3969                        "{}[({}) as usize] = {};",
3970                        array_code, index_code, value_code
3971                    )
3972                }
3973            }
3974            HirStatement::FieldAssignment {
3975                object,
3976                field,
3977                value,
3978            } => {
3979                // Look up field type for null pointer detection
3980                let field_type = ctx.get_field_type(object, field);
3981                let obj_code = self.generate_expression_with_context(object, ctx);
3982                let value_code =
3983                    self.generate_expression_with_target_type(value, ctx, field_type.as_ref());
3984
3985                // DECY-119: Check if object is a raw pointer - need unsafe deref
3986                let obj_type = if let HirExpression::Variable(name) = object {
3987                    ctx.get_type(name)
3988                } else {
3989                    None
3990                };
3991
3992                if matches!(obj_type, Some(HirType::Pointer(_))) {
3993                    // Raw pointer field assignment needs unsafe block
3994                    // DECY-143: Add SAFETY comment
3995                    Self::unsafe_stmt(
3996                        &format!("(*{}).{} = {}", obj_code, field, value_code),
3997                        "pointer is non-null and points to valid struct with exclusive access",
3998                    )
3999                } else {
4000                    // Regular struct field assignment
4001                    format!("{}.{} = {};", obj_code, field, value_code)
4002                }
4003            }
4004            HirStatement::Free { pointer } => {
4005                // free(ptr) → automatic drop via RAII
4006                // Generate a comment explaining that the memory will be deallocated automatically
4007                // when the variable goes out of scope
4008                let pointer_name = match pointer {
4009                    HirExpression::Variable(name) => name.clone(),
4010                    _ => self.generate_expression_with_context(pointer, ctx),
4011                };
4012                format!(
4013                    "// Memory for '{}' deallocated automatically by RAII",
4014                    pointer_name
4015                )
4016            }
4017            HirStatement::Expression(expr) => {
4018                // Expression statement: function calls, increments, etc. for side effects
4019                // DECY-065: Added to fix printf() and other function call statement bugs
4020                // C: printf("Hello"); → Rust: printf("Hello");
4021                format!("{};", self.generate_expression_with_context(expr, ctx))
4022            }
4023        }
4024    }
4025
4026    /// Generate a function signature from HIR.
4027    ///
4028    /// # Examples
4029    ///
4030    /// ```
4031    /// use decy_codegen::CodeGenerator;
4032    /// use decy_hir::{HirFunction, HirType};
4033    ///
4034    /// let func = HirFunction::new("test".to_string(), HirType::Void, vec![]);
4035    /// let codegen = CodeGenerator::new();
4036    /// let sig = codegen.generate_signature(&func);
4037    ///
4038    /// assert_eq!(sig, "fn test()");
4039    /// ```
4040    pub fn generate_signature(&self, func: &HirFunction) -> String {
4041        // DECY-076 GREEN: Generate lifetime annotations using LifetimeAnnotator
4042        use decy_ownership::lifetime_gen::LifetimeAnnotator;
4043        let lifetime_annotator = LifetimeAnnotator::new();
4044        let annotated_sig = lifetime_annotator.annotate_function(func);
4045
4046        let mut sig = format!("fn {}", func.name());
4047
4048        // Add lifetime parameters if needed
4049        let lifetime_syntax = lifetime_annotator.generate_lifetime_syntax(&annotated_sig.lifetimes);
4050        sig.push_str(&lifetime_syntax);
4051
4052        // DECY-096: Detect void* parameters for generic transformation
4053        use decy_analyzer::void_ptr_analysis::{TypeConstraint, VoidPtrAnalyzer};
4054        let void_analyzer = VoidPtrAnalyzer::new();
4055        let void_patterns = void_analyzer.analyze(func);
4056
4057        // DECY-168: Only consider patterns with actual constraints/types as "real" void* usage
4058        // Empty body functions (stubs) will have patterns but no constraints
4059        let has_real_void_usage = void_patterns.iter().any(|vp| {
4060            !vp.constraints.is_empty() || !vp.inferred_types.is_empty()
4061        });
4062
4063        // DECY-097: Collect trait bounds from all void* patterns
4064        let mut trait_bounds: Vec<&str> = Vec::new();
4065        for pattern in &void_patterns {
4066            for constraint in &pattern.constraints {
4067                let bound = match constraint {
4068                    TypeConstraint::PartialOrd => "PartialOrd",
4069                    TypeConstraint::PartialEq => "PartialEq",
4070                    TypeConstraint::Clone => "Clone",
4071                    TypeConstraint::Copy => "Copy",
4072                    _ => continue,
4073                };
4074                if !trait_bounds.contains(&bound) {
4075                    trait_bounds.push(bound);
4076                }
4077            }
4078        }
4079
4080        // Add generic type parameter with trait bounds if function has void* params with real usage
4081        // DECY-168: Don't add <T> for stub functions without body analysis
4082        if has_real_void_usage {
4083            if trait_bounds.is_empty() {
4084                sig.push_str("<T>");
4085            } else {
4086                sig.push_str(&format!("<T: {}>", trait_bounds.join(" + ")));
4087            }
4088        }
4089
4090        // DECY-072 GREEN: Detect array parameters using ownership analysis
4091        use decy_ownership::dataflow::DataflowAnalyzer;
4092        let analyzer = DataflowAnalyzer::new();
4093        let graph = analyzer.analyze(func);
4094
4095        // DECY-084 GREEN: Detect output parameters for transformation
4096        use decy_analyzer::output_params::{OutputParamDetector, ParameterKind};
4097        let output_detector = OutputParamDetector::new();
4098        let output_params = output_detector.detect(func);
4099
4100        // Track which parameters are length parameters to skip them
4101        let mut skip_params = std::collections::HashSet::new();
4102
4103        // DECY-084: Track output parameters to skip and use for return type
4104        let mut output_param_type: Option<HirType> = None;
4105        let mut output_is_fallible = false;
4106        for op in &output_params {
4107            if op.kind == ParameterKind::Output {
4108                skip_params.insert(op.name.clone());
4109                output_is_fallible = op.is_fallible;
4110                // Get the output parameter's inner type (pointer target)
4111                if let Some(param) = func.parameters().iter().find(|p| p.name() == op.name) {
4112                    if let HirType::Pointer(inner) = param.param_type() {
4113                        output_param_type = Some((**inner).clone());
4114                    }
4115                }
4116            }
4117        }
4118
4119        // First pass: identify array parameters and their associated length parameters
4120        // DECY-113: Only skip params with length-like names to avoid removing non-length params
4121        // DECY-162: Don't skip length param if array uses pointer arithmetic (stays as raw pointer)
4122        for (idx, param) in func.parameters().iter().enumerate() {
4123            if let Some(true) = graph.is_array_parameter(param.name()) {
4124                // DECY-162: Don't skip length param if array uses pointer arithmetic
4125                // Raw pointers don't have .len(), so we need to keep the size param
4126                if self.uses_pointer_arithmetic(func, param.name()) {
4127                    continue; // Skip adding length param to skip_params
4128                }
4129
4130                // This is an array parameter - mark the next param as length param to skip
4131                // but only if it has a length-like name
4132                if idx + 1 < func.parameters().len() {
4133                    let next_param = &func.parameters()[idx + 1];
4134                    if matches!(next_param.param_type(), HirType::Int) {
4135                        let param_name = next_param.name().to_lowercase();
4136                        // Only skip if the name suggests it's a length/size parameter
4137                        if param_name.contains("len")
4138                            || param_name.contains("size")
4139                            || param_name.contains("count")
4140                            || param_name == "n"
4141                            || param_name == "num"
4142                        {
4143                            skip_params.insert(next_param.name().to_string());
4144                        }
4145                    }
4146                }
4147            }
4148        }
4149
4150        // Generate parameters with lifetime annotations
4151        sig.push('(');
4152        let params: Vec<String> = annotated_sig
4153            .parameters
4154            .iter()
4155            .filter_map(|p| {
4156                // Skip length parameters for array parameters
4157                if skip_params.contains(&p.name) {
4158                    return None;
4159                }
4160
4161                // Check if this is an array parameter
4162                let is_array = graph.is_array_parameter(&p.name).unwrap_or(false);
4163
4164                // DECY-161: Array params with pointer arithmetic must stay as raw pointers
4165                // Slices don't support arr++ or arr + n, so check for pointer arithmetic first
4166                let uses_ptr_arithmetic = self.uses_pointer_arithmetic(func, &p.name);
4167
4168                if is_array && !uses_ptr_arithmetic {
4169                    // Transform to slice parameter (only if no pointer arithmetic)
4170                    // Find the original parameter to get the HirType
4171                    if let Some(orig_param) =
4172                        func.parameters().iter().find(|fp| fp.name() == p.name)
4173                    {
4174                        let is_mutable = self.is_parameter_modified(func, &p.name);
4175                        let slice_type =
4176                            self.pointer_to_slice_type(orig_param.param_type(), is_mutable);
4177                        // For slices, don't add 'mut' prefix (slices themselves aren't reassigned)
4178                        Some(format!("{}: {}", p.name, slice_type))
4179                    } else {
4180                        None
4181                    }
4182                } else {
4183                    // DECY-086: Check if this is an array parameter that should become a slice
4184                    // In C, `int arr[10]` as a parameter decays to a pointer, so we use slice
4185                    if let Some(orig_param) =
4186                        func.parameters().iter().find(|fp| fp.name() == p.name)
4187                    {
4188                        if let HirType::Array { element_type, .. } = orig_param.param_type() {
4189                            // Fixed-size array parameter → slice reference
4190                            let is_mutable = self.is_parameter_modified(func, &p.name);
4191                            let element_str = Self::map_type(element_type);
4192                            if is_mutable {
4193                                return Some(format!("{}: &mut [{}]", p.name, element_str));
4194                            } else {
4195                                return Some(format!("{}: &[{}]", p.name, element_str));
4196                            }
4197                        }
4198                    }
4199                    // DECY-111: Check if this is a pointer parameter that should become a reference
4200                    // DECY-123: Skip transformation if pointer arithmetic is used
4201                    if let Some(orig_param) =
4202                        func.parameters().iter().find(|fp| fp.name() == p.name)
4203                    {
4204                        // DECY-135: const char* → &str transformation
4205                        // DECY-138: Add mut for string iteration patterns (param reassignment)
4206                        // Must check BEFORE other pointer transformations
4207                        if orig_param.is_const_char_pointer() {
4208                            return Some(format!("mut {}: &str", p.name));
4209                        }
4210
4211                        if let HirType::Pointer(inner) = orig_param.param_type() {
4212                            // DECY-096: void* param becomes generic &T or &mut T
4213                            // DECY-168: Only apply generic transformation if we found an actual pattern
4214                            // for this specific parameter WITH real constraints (from body analysis).
4215                            // Otherwise keep as raw pointer *mut ().
4216                            if matches!(**inner, HirType::Void) {
4217                                // Look for a void pattern specifically for this parameter
4218                                // that has actual constraints (indicating real usage in body)
4219                                let void_pattern = void_patterns.iter().find(|vp| {
4220                                    vp.param_name == p.name
4221                                        && (!vp.constraints.is_empty()
4222                                            || !vp.inferred_types.is_empty())
4223                                });
4224
4225                                if let Some(pattern) = void_pattern {
4226                                    // Found actual usage pattern - apply generic transformation
4227                                    let is_mutable = pattern.constraints.contains(
4228                                        &decy_analyzer::void_ptr_analysis::TypeConstraint::Mutable,
4229                                    );
4230                                    if is_mutable {
4231                                        return Some(format!("{}: &mut T", p.name));
4232                                    } else {
4233                                        return Some(format!("{}: &T", p.name));
4234                                    }
4235                                } else {
4236                                    // DECY-168: No pattern with real constraints found - keep as raw pointer
4237                                    // This is important for stdlib stubs (realloc, memcpy, etc.)
4238                                    return Some(format!("{}: *mut ()", p.name));
4239                                }
4240                            }
4241                            // DECY-134: Check for string iteration pattern FIRST
4242                            // char* with pointer arithmetic → slice instead of raw pointer
4243                            if self.is_string_iteration_param(func, &p.name) {
4244                                // Transform to slice for safe string iteration
4245                                let is_mutable = self.is_parameter_deref_modified(func, &p.name);
4246                                if is_mutable {
4247                                    return Some(format!("{}: &mut [u8]", p.name));
4248                                } else {
4249                                    return Some(format!("{}: &[u8]", p.name));
4250                                }
4251                            }
4252                            // DECY-123: Don't transform to reference if pointer arithmetic is used
4253                            // (e.g., ptr = ptr + 1) - keep as raw pointer
4254                            if self.uses_pointer_arithmetic(func, &p.name) {
4255                                // Keep as raw pointer - will need unsafe blocks
4256                                // DECY-124: Add mut since the pointer is reassigned
4257                                let inner_type = Self::map_type(inner);
4258                                return Some(format!("mut {}: *mut {}", p.name, inner_type));
4259                            }
4260                            // Transform pointer param to mutable reference
4261                            // Check if the param is modified in the function body
4262                            let is_mutable = self.is_parameter_deref_modified(func, &p.name);
4263                            let inner_type = Self::map_type(inner);
4264                            if is_mutable {
4265                                return Some(format!("{}: &mut {}", p.name, inner_type));
4266                            } else {
4267                                // Read-only pointer becomes immutable reference
4268                                return Some(format!("{}: &{}", p.name, inner_type));
4269                            }
4270                        }
4271                    }
4272                    // Regular parameter with lifetime annotation
4273                    let type_str = self.annotated_type_to_string(&p.param_type);
4274                    // In C, parameters are mutable by default (can be reassigned)
4275                    Some(format!("mut {}: {}", p.name, type_str))
4276                }
4277            })
4278            .collect();
4279        sig.push_str(&params.join(", "));
4280        sig.push(')');
4281
4282        // Special handling for main function (DECY-AUDIT-001)
4283        // C's int main() must become Rust's fn main() (no return type)
4284        // Rust's entry point returns () and uses std::process::exit(N) for exit codes
4285        if func.name() == "main" && matches!(func.return_type(), HirType::Int) {
4286            // Skip return type for main - it must be fn main()
4287            return sig;
4288        }
4289
4290        // DECY-084 GREEN: Generate return type considering output parameters
4291        // Priority: output param type > original return type
4292        if let Some(out_type) = output_param_type {
4293            let out_type_str = Self::map_type(&out_type);
4294            if output_is_fallible {
4295                // Fallible function: int func(..., T* out) -> Result<T, i32>
4296                sig.push_str(&format!(" -> Result<{}, i32>", out_type_str));
4297            } else {
4298                // Non-fallible void function: void func(..., T* out) -> T
4299                sig.push_str(&format!(" -> {}", out_type_str));
4300            }
4301        } else {
4302            // DECY-142: Check if function returns malloc'd array → use Vec<T>
4303            if let Some(vec_element_type) = self.detect_vec_return(func) {
4304                let element_type_str = Self::map_type(&vec_element_type);
4305                sig.push_str(&format!(" -> Vec<{}>", element_type_str));
4306            } else {
4307                // Generate return type with lifetime annotation (skip for void)
4308                if !matches!(
4309                    &annotated_sig.return_type,
4310                    AnnotatedType::Simple(HirType::Void)
4311                ) {
4312                    let return_type_str = self.annotated_type_to_string(&annotated_sig.return_type);
4313                    sig.push_str(&format!(" -> {}", return_type_str));
4314                }
4315            }
4316        }
4317
4318        sig
4319    }
4320
4321    /// DECY-142: Check if function returns a malloc-allocated array.
4322    /// Returns Some(element_type) if the function allocates with malloc and returns it.
4323    /// This pattern should use Vec<T> return type instead of *mut T.
4324    fn detect_vec_return(&self, func: &HirFunction) -> Option<HirType> {
4325        // Only applies to functions returning pointer types
4326        let return_type = func.return_type();
4327        let element_type = match return_type {
4328            HirType::Pointer(inner) => inner.as_ref().clone(),
4329            _ => return None,
4330        };
4331
4332        // Look for pattern: var = malloc(...); return var;
4333        // or: return malloc(...);
4334        let mut malloc_vars: std::collections::HashSet<String> = std::collections::HashSet::new();
4335
4336        for stmt in func.body() {
4337            // Track variables assigned from malloc
4338            if let HirStatement::VariableDeclaration {
4339                name,
4340                initializer: Some(init_expr),
4341                ..
4342            } = stmt
4343            {
4344                if Self::is_malloc_call(init_expr) {
4345                    malloc_vars.insert(name.clone());
4346                }
4347            }
4348
4349            // Check return statements
4350            if let HirStatement::Return(Some(ret_expr)) = stmt {
4351                // Direct return of malloc
4352                if Self::is_malloc_call(ret_expr) {
4353                    return Some(element_type);
4354                }
4355                // Return of a variable that was assigned from malloc
4356                if let HirExpression::Variable(var_name) = ret_expr {
4357                    if malloc_vars.contains(var_name) {
4358                        return Some(element_type);
4359                    }
4360                }
4361            }
4362        }
4363
4364        None
4365    }
4366
4367    /// Helper to check if an expression is ANY malloc or calloc call (including through casts).
4368    /// DECY-220: This is used for type annotation transformation (*mut T → Vec<T>).
4369    /// Unlike `is_malloc_call`, this returns true for ANY malloc/calloc, not just array patterns.
4370    fn is_any_malloc_or_calloc(expr: &HirExpression) -> bool {
4371        match expr {
4372            HirExpression::Malloc { .. } => true,
4373            HirExpression::Calloc { .. } => true,
4374            HirExpression::FunctionCall { function, .. }
4375                if function == "malloc" || function == "calloc" =>
4376            {
4377                true
4378            }
4379            // DECY-220: Check through cast expressions (e.g., (int*)malloc(...))
4380            HirExpression::Cast { expr: inner, .. } => Self::is_any_malloc_or_calloc(inner),
4381            _ => false,
4382        }
4383    }
4384
4385    /// Helper to check if an expression is a malloc call for ARRAY allocation.
4386    /// DECY-142: Only returns true for array allocations (malloc(n * sizeof(T))),
4387    /// not single struct allocations (malloc(sizeof(T))).
4388    fn is_malloc_call(expr: &HirExpression) -> bool {
4389        match expr {
4390            HirExpression::FunctionCall {
4391                function,
4392                arguments,
4393                ..
4394            } if function == "malloc" => {
4395                // Check if this is an array allocation: malloc(n * sizeof(T))
4396                // Single struct allocation: malloc(sizeof(T)) should NOT match
4397                if arguments.len() == 1 {
4398                    Self::is_array_allocation_size(&arguments[0])
4399                } else {
4400                    false
4401                }
4402            }
4403            HirExpression::Malloc { size } => {
4404                // Check if this is an array allocation
4405                Self::is_array_allocation_size(size)
4406            }
4407            // DECY-142: Check through cast expressions (e.g., (int*)malloc(...))
4408            HirExpression::Cast { expr: inner, .. } => Self::is_malloc_call(inner),
4409            _ => false,
4410        }
4411    }
4412
4413    /// Check if a malloc size expression indicates array allocation (n * sizeof(T))
4414    /// vs single struct allocation (sizeof(T) or constant).
4415    fn is_array_allocation_size(size_expr: &HirExpression) -> bool {
4416        match size_expr {
4417            // n * sizeof(T) pattern - this is array allocation
4418            HirExpression::BinaryOp {
4419                op: decy_hir::BinaryOperator::Multiply,
4420                ..
4421            } => true,
4422            // sizeof(T) alone - this is single struct allocation, NOT array
4423            HirExpression::Sizeof { .. } => false,
4424            // Constant - likely single allocation
4425            HirExpression::IntLiteral(_) => false,
4426            // Variable could be array size, but be conservative
4427            HirExpression::Variable(_) => false,
4428            // Recurse through casts
4429            HirExpression::Cast { expr: inner, .. } => Self::is_array_allocation_size(inner),
4430            // Other expressions - be conservative, assume not array
4431            _ => false,
4432        }
4433    }
4434
4435    /// Check if a parameter is modified in the function body (DECY-072 GREEN).
4436    ///
4437    /// Used to determine whether to use `&[T]` or `&mut [T]` for array parameters.
4438    fn is_parameter_modified(&self, func: &HirFunction, param_name: &str) -> bool {
4439        // Check if the parameter is used in any assignment statements
4440        for stmt in func.body() {
4441            if self.statement_modifies_variable(stmt, param_name) {
4442                return true;
4443            }
4444        }
4445        false
4446    }
4447
4448    /// Check if a pointer parameter is dereferenced and modified (DECY-111 GREEN).
4449    ///
4450    /// Used to determine whether to use `&T` or `&mut T` for pointer parameters.
4451    /// Returns true if the parameter is used in:
4452    /// - `*ptr = value;` (DerefAssignment)
4453    /// - `ptr[i] = value;` (ArrayIndexAssignment with pointer)
4454    fn is_parameter_deref_modified(&self, func: &HirFunction, param_name: &str) -> bool {
4455        for stmt in func.body() {
4456            if self.statement_deref_modifies_variable(stmt, param_name) {
4457                return true;
4458            }
4459        }
4460        false
4461    }
4462
4463    /// Recursively check if a statement deref-modifies a variable (DECY-111 GREEN).
4464    #[allow(clippy::only_used_in_recursion)]
4465    fn statement_deref_modifies_variable(&self, stmt: &HirStatement, var_name: &str) -> bool {
4466        match stmt {
4467            HirStatement::DerefAssignment { target, .. } => {
4468                // Check if this is *ptr = value where ptr is our variable
4469                if let HirExpression::Variable(name) = target {
4470                    return name == var_name;
4471                }
4472                false
4473            }
4474            HirStatement::ArrayIndexAssignment { array, .. } => {
4475                // Check if this is ptr[i] = value where ptr is our variable
4476                if let HirExpression::Variable(name) = &**array {
4477                    return name == var_name;
4478                }
4479                false
4480            }
4481            HirStatement::Assignment { .. } => {
4482                // Regular variable assignment (src = src + 1) does NOT modify *src
4483                // Only DerefAssignment (*src = value) modifies the pointed-to value
4484                false
4485            }
4486            HirStatement::If {
4487                then_block,
4488                else_block,
4489                ..
4490            } => {
4491                then_block
4492                    .iter()
4493                    .any(|s| self.statement_deref_modifies_variable(s, var_name))
4494                    || else_block.as_ref().is_some_and(|blk| {
4495                        blk.iter()
4496                            .any(|s| self.statement_deref_modifies_variable(s, var_name))
4497                    })
4498            }
4499            HirStatement::While { body, .. } | HirStatement::For { body, .. } => body
4500                .iter()
4501                .any(|s| self.statement_deref_modifies_variable(s, var_name)),
4502            _ => false,
4503        }
4504    }
4505
4506    /// Check if a parameter uses pointer arithmetic, is reassigned, or compared to NULL (DECY-123, DECY-137).
4507    ///
4508    /// Used to determine whether a pointer parameter should remain a raw pointer
4509    /// instead of being transformed to a reference.
4510    /// Returns true if the parameter is used in:
4511    /// - `ptr = ptr + n;` (pointer arithmetic assignment)
4512    /// - `ptr = ptr - n;` (pointer arithmetic assignment)
4513    /// - `ptr += n;` or `ptr -= n;` (compound pointer arithmetic)
4514    /// - `ptr = ptr->field;` (DECY-137: linked list traversal pattern)
4515    /// - `ptr = other_ptr;` (any pointer reassignment)
4516    /// - `ptr != 0` or `ptr == 0` (DECY-137: NULL comparison - Rust refs can't be null)
4517    ///
4518    /// References in Rust cannot be reassigned or null, so any pointer param that is
4519    /// reassigned or NULL-checked must remain as a raw pointer.
4520    fn uses_pointer_arithmetic(&self, func: &HirFunction, param_name: &str) -> bool {
4521        for stmt in func.body() {
4522            if self.statement_uses_pointer_arithmetic(stmt, param_name) {
4523                return true;
4524            }
4525            // DECY-137: Also check for NULL comparisons in conditions
4526            if self.statement_uses_null_comparison(stmt, param_name) {
4527                return true;
4528            }
4529        }
4530        false
4531    }
4532
4533    /// Check if a statement contains NULL comparison for a variable (DECY-137).
4534    ///
4535    /// If a pointer is compared to NULL (0), it should stay as raw pointer
4536    /// because Rust references can never be null.
4537    #[allow(clippy::only_used_in_recursion)]
4538    fn statement_uses_null_comparison(&self, stmt: &HirStatement, var_name: &str) -> bool {
4539        match stmt {
4540            HirStatement::If {
4541                condition,
4542                then_block,
4543                else_block,
4544                ..
4545            } => {
4546                // Check condition for NULL comparison
4547                if self.expression_compares_to_null(condition, var_name) {
4548                    return true;
4549                }
4550                // Recursively check nested statements
4551                then_block
4552                    .iter()
4553                    .any(|s| self.statement_uses_null_comparison(s, var_name))
4554                    || else_block.as_ref().is_some_and(|blk| {
4555                        blk.iter()
4556                            .any(|s| self.statement_uses_null_comparison(s, var_name))
4557                    })
4558            }
4559            HirStatement::While {
4560                condition, body, ..
4561            } => {
4562                if self.expression_compares_to_null(condition, var_name) {
4563                    return true;
4564                }
4565                body.iter()
4566                    .any(|s| self.statement_uses_null_comparison(s, var_name))
4567            }
4568            HirStatement::For {
4569                condition, body, ..
4570            } => {
4571                if self.expression_compares_to_null(condition, var_name) {
4572                    return true;
4573                }
4574                body.iter()
4575                    .any(|s| self.statement_uses_null_comparison(s, var_name))
4576            }
4577            _ => false,
4578        }
4579    }
4580
4581    /// Check if an expression compares a variable to NULL (0).
4582    fn expression_compares_to_null(&self, expr: &HirExpression, var_name: &str) -> bool {
4583        match expr {
4584            HirExpression::BinaryOp { op, left, right } => {
4585                if matches!(op, BinaryOperator::Equal | BinaryOperator::NotEqual) {
4586                    // Check: var == 0 or var != 0
4587                    if let HirExpression::Variable(name) = &**left {
4588                        if name == var_name
4589                            && matches!(
4590                                **right,
4591                                HirExpression::IntLiteral(0) | HirExpression::NullLiteral
4592                            )
4593                        {
4594                            return true;
4595                        }
4596                    }
4597                    // Check: 0 == var or 0 != var
4598                    if let HirExpression::Variable(name) = &**right {
4599                        if name == var_name
4600                            && matches!(
4601                                **left,
4602                                HirExpression::IntLiteral(0) | HirExpression::NullLiteral
4603                            )
4604                        {
4605                            return true;
4606                        }
4607                    }
4608                }
4609                // Recursively check nested expressions (e.g., in logical AND/OR)
4610                self.expression_compares_to_null(left, var_name)
4611                    || self.expression_compares_to_null(right, var_name)
4612            }
4613            _ => false,
4614        }
4615    }
4616
4617    /// Recursively check if a statement uses pointer arithmetic or reassigns a variable (DECY-123, DECY-137).
4618    #[allow(clippy::only_used_in_recursion)]
4619    fn statement_uses_pointer_arithmetic(&self, stmt: &HirStatement, var_name: &str) -> bool {
4620        match stmt {
4621            HirStatement::Assignment { target, value } => {
4622                // DECY-137: Any assignment to the pointer parameter means it must stay as raw pointer
4623                // This catches:
4624                // - ptr = ptr + n (pointer arithmetic)
4625                // - ptr = ptr->next (linked list traversal)
4626                // - ptr = other_ptr (general reassignment)
4627                //
4628                // References cannot be reassigned, only raw pointers can.
4629                if target == var_name {
4630                    // Check if this is pointer arithmetic (ptr = ptr + n or ptr = ptr - n)
4631                    if let HirExpression::BinaryOp { op, left, .. } = value {
4632                        if matches!(op, BinaryOperator::Add | BinaryOperator::Subtract) {
4633                            if let HirExpression::Variable(name) = &**left {
4634                                if name == var_name {
4635                                    return true;
4636                                }
4637                            }
4638                        }
4639                    }
4640
4641                    // DECY-137: Check for field access reassignment (ptr = ptr->field)
4642                    // This is the linked list traversal pattern: head = head->next
4643                    if let HirExpression::PointerFieldAccess { pointer, .. } = value {
4644                        if let HirExpression::Variable(name) = &**pointer {
4645                            if name == var_name {
4646                                return true;
4647                            }
4648                        }
4649                    }
4650
4651                    // DECY-137: Check for any other pointer reassignment
4652                    // If ptr is assigned from another variable or expression, it needs
4653                    // to stay as raw pointer. However, we need to be careful not to
4654                    // flag initialization (which happens at declaration, not assignment).
4655                    // For now, flag field access from ANY pointer as reassignment.
4656                    if matches!(value, HirExpression::PointerFieldAccess { .. }) {
4657                        return true;
4658                    }
4659                }
4660                false
4661            }
4662            HirStatement::If {
4663                then_block,
4664                else_block,
4665                ..
4666            } => {
4667                then_block
4668                    .iter()
4669                    .any(|s| self.statement_uses_pointer_arithmetic(s, var_name))
4670                    || else_block.as_ref().is_some_and(|blk| {
4671                        blk.iter()
4672                            .any(|s| self.statement_uses_pointer_arithmetic(s, var_name))
4673                    })
4674            }
4675            // DECY-164: Check for post/pre increment/decrement on the variable
4676            HirStatement::Expression(expr) => {
4677                Self::expression_uses_pointer_arithmetic_static(expr, var_name)
4678            }
4679            HirStatement::While { body, .. } | HirStatement::For { body, .. } => body
4680                .iter()
4681                .any(|s| self.statement_uses_pointer_arithmetic(s, var_name)),
4682            _ => false,
4683        }
4684    }
4685
4686    /// DECY-164: Check if an expression uses pointer arithmetic on a variable.
4687    /// Catches str++, ++str, str--, --str patterns.
4688    fn expression_uses_pointer_arithmetic_static(expr: &HirExpression, var_name: &str) -> bool {
4689        match expr {
4690            HirExpression::PostIncrement { operand }
4691            | HirExpression::PreIncrement { operand }
4692            | HirExpression::PostDecrement { operand }
4693            | HirExpression::PreDecrement { operand } => {
4694                matches!(&**operand, HirExpression::Variable(name) if name == var_name)
4695            }
4696            _ => false,
4697        }
4698    }
4699
4700    /// DECY-134b: Get all string iteration params for a function.
4701    ///
4702    /// Returns a list of (param_index, is_mutable) for each char* param that uses pointer arithmetic.
4703    /// Used by decy-core to build string_iter_funcs info for call site transformation.
4704    pub fn get_string_iteration_params(&self, func: &HirFunction) -> Vec<(usize, bool)> {
4705        func.parameters()
4706            .iter()
4707            .enumerate()
4708            .filter_map(|(i, param)| {
4709                if self.is_string_iteration_param(func, param.name()) {
4710                    let is_mutable = self.is_parameter_deref_modified(func, param.name());
4711                    Some((i, is_mutable))
4712                } else {
4713                    None
4714                }
4715            })
4716            .collect()
4717    }
4718
4719    /// DECY-134: Check if a char* parameter is used in a string iteration pattern.
4720    ///
4721    /// String iteration pattern: char* with pointer arithmetic in a loop (while (*s) { s++; })
4722    /// These should be transformed to slice + index for safe Rust.
4723    /// DECY-164: Skip if function uses pointer subtraction (e.g., str - start for length calculation).
4724    fn is_string_iteration_param(&self, func: &HirFunction, param_name: &str) -> bool {
4725        // Must be a char pointer (Pointer(Char))
4726        let is_char_ptr = func.parameters().iter().any(|p| {
4727            p.name() == param_name
4728                && matches!(p.param_type(), HirType::Pointer(inner) if matches!(&**inner, HirType::Char))
4729        });
4730
4731        if !is_char_ptr {
4732            return false;
4733        }
4734
4735        // DECY-164: Don't apply string iteration transformation if there's pointer subtraction
4736        // Pointer subtraction (str - start) requires raw pointers, can't use slices
4737        if self.function_uses_pointer_subtraction(func, param_name) {
4738            return false;
4739        }
4740
4741        // Must use pointer arithmetic
4742        self.uses_pointer_arithmetic(func, param_name)
4743    }
4744
4745    /// DECY-164: Check if a function uses pointer subtraction involving a variable.
4746    /// Pattern: var - other_ptr (e.g., str - start for calculating string length)
4747    fn function_uses_pointer_subtraction(&self, func: &HirFunction, var_name: &str) -> bool {
4748        for stmt in func.body() {
4749            if self.statement_uses_pointer_subtraction(stmt, var_name) {
4750                return true;
4751            }
4752        }
4753        false
4754    }
4755
4756    /// DECY-164: Check if a statement uses pointer subtraction involving a variable.
4757    fn statement_uses_pointer_subtraction(&self, stmt: &HirStatement, var_name: &str) -> bool {
4758        match stmt {
4759            HirStatement::Return(Some(expr)) => {
4760                self.expression_uses_pointer_subtraction(expr, var_name)
4761            }
4762            HirStatement::Assignment { value, .. } => {
4763                self.expression_uses_pointer_subtraction(value, var_name)
4764            }
4765            HirStatement::VariableDeclaration { initializer, .. } => initializer
4766                .as_ref()
4767                .map(|e| self.expression_uses_pointer_subtraction(e, var_name))
4768                .unwrap_or(false),
4769            HirStatement::If {
4770                condition,
4771                then_block,
4772                else_block,
4773                ..
4774            } => {
4775                self.expression_uses_pointer_subtraction(condition, var_name)
4776                    || then_block
4777                        .iter()
4778                        .any(|s| self.statement_uses_pointer_subtraction(s, var_name))
4779                    || else_block.as_ref().is_some_and(|blk| {
4780                        blk.iter()
4781                            .any(|s| self.statement_uses_pointer_subtraction(s, var_name))
4782                    })
4783            }
4784            HirStatement::While { condition, body } => {
4785                self.expression_uses_pointer_subtraction(condition, var_name)
4786                    || body
4787                        .iter()
4788                        .any(|s| self.statement_uses_pointer_subtraction(s, var_name))
4789            }
4790            HirStatement::For { body, .. } => body
4791                .iter()
4792                .any(|s| self.statement_uses_pointer_subtraction(s, var_name)),
4793            _ => false,
4794        }
4795    }
4796
4797    /// DECY-164: Check if an expression uses pointer subtraction involving a variable.
4798    fn expression_uses_pointer_subtraction(&self, expr: &HirExpression, var_name: &str) -> bool {
4799        match expr {
4800            HirExpression::BinaryOp { op, left, right } => {
4801                // Check for var - other_ptr pattern
4802                if matches!(op, BinaryOperator::Subtract) {
4803                    if let HirExpression::Variable(name) = &**left {
4804                        if name == var_name {
4805                            return true;
4806                        }
4807                    }
4808                    if let HirExpression::Variable(name) = &**right {
4809                        if name == var_name {
4810                            return true;
4811                        }
4812                    }
4813                }
4814                // Recursively check subexpressions
4815                self.expression_uses_pointer_subtraction(left, var_name)
4816                    || self.expression_uses_pointer_subtraction(right, var_name)
4817            }
4818            HirExpression::Dereference(inner) => {
4819                self.expression_uses_pointer_subtraction(inner, var_name)
4820            }
4821            HirExpression::Cast { expr, .. } => {
4822                self.expression_uses_pointer_subtraction(expr, var_name)
4823            }
4824            _ => false,
4825        }
4826    }
4827
4828    /// Recursively check if a statement modifies a variable (DECY-072 GREEN).
4829    #[allow(clippy::only_used_in_recursion)]
4830    fn statement_modifies_variable(&self, stmt: &HirStatement, var_name: &str) -> bool {
4831        match stmt {
4832            HirStatement::ArrayIndexAssignment { array, .. } => {
4833                // Check if this is arr[i] = value where arr is our variable
4834                if let HirExpression::Variable(name) = &**array {
4835                    return name == var_name;
4836                }
4837                false
4838            }
4839            HirStatement::DerefAssignment { target, .. } => {
4840                // Check if this is *ptr = value where ptr is our variable
4841                if let HirExpression::Variable(name) = target {
4842                    return name == var_name;
4843                }
4844                false
4845            }
4846            HirStatement::If {
4847                then_block,
4848                else_block,
4849                ..
4850            } => {
4851                then_block
4852                    .iter()
4853                    .any(|s| self.statement_modifies_variable(s, var_name))
4854                    || else_block.as_ref().is_some_and(|blk| {
4855                        blk.iter()
4856                            .any(|s| self.statement_modifies_variable(s, var_name))
4857                    })
4858            }
4859            HirStatement::While { body, .. } | HirStatement::For { body, .. } => body
4860                .iter()
4861                .any(|s| self.statement_modifies_variable(s, var_name)),
4862            _ => false,
4863        }
4864    }
4865
4866    /// Convert a pointer type to a slice type (DECY-072 GREEN).
4867    ///
4868    /// Transforms `*mut T` or `*const T` to `&\[T]` or `&mut \[T]`.
4869    fn pointer_to_slice_type(&self, ptr_type: &HirType, is_mutable: bool) -> String {
4870        if let HirType::Pointer(inner) = ptr_type {
4871            let element_type = Self::map_type(inner);
4872            if is_mutable {
4873                format!("&mut [{}]", element_type)
4874            } else {
4875                format!("&[{}]", element_type)
4876            }
4877        } else {
4878            // Fallback: not a pointer, use normal mapping
4879            Self::map_type(ptr_type)
4880        }
4881    }
4882
4883    /// Transform length parameter references to array.len() calls (DECY-072 GREEN).
4884    ///
4885    /// Replaces variable references like `len` with `arr.len()` in generated code.
4886    fn transform_length_refs(
4887        &self,
4888        code: &str,
4889        length_to_array: &std::collections::HashMap<String, String>,
4890    ) -> String {
4891        let mut result = code.to_string();
4892
4893        // Replace each length parameter reference with corresponding array.len() call
4894        for (length_param, array_param) in length_to_array {
4895            // Match the length parameter as a standalone identifier
4896            // Use word boundaries to avoid partial matches
4897            // Common patterns: "return len", "x + len", "len)", etc.
4898            let patterns = vec![
4899                (
4900                    format!("return {}", length_param),
4901                    format!("return {}.len() as i32", array_param),
4902                ),
4903                (
4904                    format!("{} ", length_param),
4905                    format!("{}.len() as i32 ", array_param),
4906                ),
4907                (
4908                    format!("{})", length_param),
4909                    format!("{}.len() as i32)", array_param),
4910                ),
4911                (
4912                    format!("{},", length_param),
4913                    format!("{}.len() as i32,", array_param),
4914                ),
4915                (
4916                    format!("{}]", length_param),
4917                    format!("{}.len() as i32]", array_param),
4918                ),
4919                (
4920                    length_param.clone() + "}",
4921                    array_param.clone() + ".len() as i32}",
4922                ),
4923                (
4924                    format!("{};", length_param),
4925                    format!("{}.len() as i32;", array_param),
4926                ),
4927            ];
4928
4929            for (pattern, replacement) in patterns {
4930                result = result.replace(&pattern, &replacement);
4931            }
4932        }
4933
4934        result
4935    }
4936
4937    /// Generate a function signature with lifetime annotations.
4938    ///
4939    /// Takes an `AnnotatedSignature` with lifetime information and generates
4940    /// the complete Rust function signature including lifetime parameters.
4941    ///
4942    /// # Examples
4943    ///
4944    /// ```
4945    /// use decy_codegen::CodeGenerator;
4946    /// use decy_ownership::lifetime_gen::{AnnotatedSignature, AnnotatedParameter, AnnotatedType, LifetimeParam};
4947    /// use decy_hir::HirType;
4948    ///
4949    /// let sig = AnnotatedSignature {
4950    ///     name: "get_first".to_string(),
4951    ///     lifetimes: vec![LifetimeParam::standard(0)], // 'a
4952    ///     parameters: vec![
4953    ///         AnnotatedParameter {
4954    ///             name: "items".to_string(),
4955    ///             param_type: AnnotatedType::Reference {
4956    ///                 inner: Box::new(AnnotatedType::Simple(HirType::Int)),
4957    ///                 mutable: false,
4958    ///                 lifetime: Some(LifetimeParam::standard(0)),
4959    ///             },
4960    ///         },
4961    ///     ],
4962    ///     return_type: AnnotatedType::Reference {
4963    ///         inner: Box::new(AnnotatedType::Simple(HirType::Int)),
4964    ///         mutable: false,
4965    ///         lifetime: Some(LifetimeParam::standard(0)),
4966    ///     },
4967    /// };
4968    ///
4969    /// let codegen = CodeGenerator::new();
4970    /// let rust_sig = codegen.generate_annotated_signature(&sig);
4971    ///
4972    /// assert!(rust_sig.contains("<'a>"));
4973    /// assert!(rust_sig.contains("&'a i32"));
4974    /// ```
4975    pub fn generate_annotated_signature(&self, sig: &AnnotatedSignature) -> String {
4976        self.generate_annotated_signature_with_func(sig, None)
4977    }
4978
4979    /// Generate a function signature from an annotated signature with optional function body access.
4980    ///
4981    /// When `func` is provided, pointer arithmetic detection is enabled (DECY-123).
4982    /// DECY-084: Also detects output parameters for transformation to return values.
4983    pub fn generate_annotated_signature_with_func(
4984        &self,
4985        sig: &AnnotatedSignature,
4986        func: Option<&HirFunction>,
4987    ) -> String {
4988        let mut result = format!("fn {}", sig.name);
4989
4990        // DECY-084/085: Detect output parameters for transformation
4991        // DECY-085: Support multiple output params as tuple
4992        use decy_analyzer::output_params::{OutputParamDetector, ParameterKind};
4993        let mut skip_output_params = std::collections::HashSet::new();
4994        let mut output_param_types: Vec<HirType> = Vec::new(); // DECY-085: collect ALL output types
4995        let mut output_is_fallible = false;
4996
4997        if let Some(f) = func {
4998            let output_detector = OutputParamDetector::new();
4999            let output_params = output_detector.detect(f);
5000
5001            // Count non-pointer parameters (inputs)
5002            let input_param_count = f
5003                .parameters()
5004                .iter()
5005                .filter(|p| !matches!(p.param_type(), HirType::Pointer(_)))
5006                .count();
5007
5008            // Count potential output params for heuristic
5009            let output_param_count = output_params
5010                .iter()
5011                .filter(|op| op.kind == ParameterKind::Output)
5012                .count();
5013
5014            for op in &output_params {
5015                if op.kind == ParameterKind::Output {
5016                    // Heuristic: Only treat as output param if:
5017                    // 1. There are other input parameters (output is derived from inputs)
5018                    // 2. Or, the name suggests it's an output (result, out, output, ret, etc.)
5019                    // 3. DECY-085: Or, there are multiple output params (void func with multiple outs)
5020                    let is_output_name = {
5021                        let name_lower = op.name.to_lowercase();
5022                        name_lower.contains("result")
5023                            || name_lower.contains("out")
5024                            || name_lower.contains("ret")
5025                            || name_lower == "len"
5026                            || name_lower == "size"
5027                            // Common dimension/coordinate names
5028                            || name_lower == "x"
5029                            || name_lower == "y"
5030                            || name_lower == "z"
5031                            || name_lower == "w"
5032                            || name_lower == "h"
5033                            || name_lower == "width"
5034                            || name_lower == "height"
5035                            || name_lower == "r"
5036                            || name_lower == "g"
5037                            || name_lower == "b"
5038                            || name_lower == "count"
5039                            || name_lower == "avg"
5040                    };
5041
5042                    if input_param_count > 0 || is_output_name || output_param_count >= 2 {
5043                        skip_output_params.insert(op.name.clone());
5044                        output_is_fallible = op.is_fallible;
5045                        // DECY-085: Collect all output parameter types
5046                        if let Some(param) = f.parameters().iter().find(|p| p.name() == op.name) {
5047                            if let HirType::Pointer(inner) = param.param_type() {
5048                                output_param_types.push((**inner).clone());
5049                            }
5050                        }
5051                    }
5052                }
5053            }
5054        }
5055
5056        // DECY-072: Check if we have any non-slice reference parameters that need lifetimes
5057        // Slices have elided lifetimes and don't need explicit lifetime parameters
5058        let has_non_slice_references = sig.parameters.iter().any(|p| {
5059            match &p.param_type {
5060                AnnotatedType::Reference { inner, .. } => {
5061                    // Check if this is NOT a slice (slice = Reference to Array with size=None)
5062                    !matches!(
5063                        &**inner,
5064                        AnnotatedType::Simple(HirType::Array { size: None, .. })
5065                    )
5066                }
5067                _ => false,
5068            }
5069        });
5070
5071        // Add lifetime parameters only if we have non-slice references
5072        if !sig.lifetimes.is_empty() && has_non_slice_references {
5073            let lifetime_params: Vec<String> =
5074                sig.lifetimes.iter().map(|lt| lt.name.clone()).collect();
5075            result.push_str(&format!("<{}>", lifetime_params.join(", ")));
5076        }
5077
5078        // Add function parameters (DECY-084: filter out output params)
5079        result.push('(');
5080        let params: Vec<String> = sig
5081            .parameters
5082            .iter()
5083            .filter(|p| !skip_output_params.contains(&p.name))
5084            .map(|p| {
5085                // Check if this is a slice parameter (Reference to Array with size=None)
5086                let is_slice = match &p.param_type {
5087                    AnnotatedType::Reference { inner, .. } => match &**inner {
5088                        AnnotatedType::Simple(HirType::Array { size, .. }) => size.is_none(),
5089                        _ => false,
5090                    },
5091                    _ => false,
5092                };
5093
5094                if is_slice {
5095                    // DECY-072: Slices don't need 'mut' prefix or explicit lifetimes
5096                    // Generate simple slice type without lifetime annotations
5097                    let type_str = match &p.param_type {
5098                        AnnotatedType::Reference { inner, mutable, .. } => {
5099                            if let AnnotatedType::Simple(HirType::Array { element_type, .. }) =
5100                                &**inner
5101                            {
5102                                if *mutable {
5103                                    format!("&mut [{}]", Self::map_type(element_type))
5104                                } else {
5105                                    format!("&[{}]", Self::map_type(element_type))
5106                                }
5107                            } else {
5108                                self.annotated_type_to_string(&p.param_type)
5109                            }
5110                        }
5111                        _ => self.annotated_type_to_string(&p.param_type),
5112                    };
5113                    format!("{}: {}", p.name, type_str)
5114                } else {
5115                    // DECY-111: Transform pointer parameters to mutable references
5116                    // DECY-123: Skip transformation if pointer arithmetic is used
5117                    // Check if param type is a simple pointer (not already a reference)
5118                    if let AnnotatedType::Simple(HirType::Pointer(inner)) = &p.param_type {
5119                        // DECY-135: const char* → &str transformation
5120                        // DECY-138: Add mut for string iteration patterns (param reassignment)
5121                        // Must check BEFORE other pointer transformations
5122                        if let Some(f) = func {
5123                            if let Some(orig_param) =
5124                                f.parameters().iter().find(|fp| fp.name() == p.name)
5125                            {
5126                                if orig_param.is_const_char_pointer() {
5127                                    return format!("mut {}: &str", p.name);
5128                                }
5129                            }
5130                        }
5131                        // DECY-134: Check for string iteration pattern FIRST
5132                        if let Some(f) = func {
5133                            if self.is_string_iteration_param(f, &p.name) {
5134                                // Transform to slice for safe string iteration
5135                                let is_mutable = self.is_parameter_deref_modified(f, &p.name);
5136                                if is_mutable {
5137                                    return format!("{}: &mut [u8]", p.name);
5138                                } else {
5139                                    return format!("{}: &[u8]", p.name);
5140                                }
5141                            }
5142                        }
5143                        // DECY-123: If we have function body access, check for pointer arithmetic
5144                        if let Some(f) = func {
5145                            if self.uses_pointer_arithmetic(f, &p.name) {
5146                                // Keep as raw pointer - needs pointer arithmetic
5147                                // DECY-124: Add mut since the pointer is reassigned
5148                                let inner_type = Self::map_type(inner);
5149                                return format!("mut {}: *mut {}", p.name, inner_type);
5150                            }
5151                        }
5152                        // DECY-168: void* parameters should stay as raw pointers
5153                        // unless they have actual usage patterns (constraints/types)
5154                        if matches!(**inner, HirType::Void) {
5155                            // Keep void* as raw pointer for stdlib stubs
5156                            return format!("{}: *mut ()", p.name);
5157                        }
5158                        // Transform *mut T → &mut T for safety
5159                        // All pointer params become &mut since C allows writing through them
5160                        let inner_type = Self::map_type(inner);
5161                        return format!("{}: &mut {}", p.name, inner_type);
5162                    }
5163                    // DECY-196: Handle unsized array parameters → slice references
5164                    // C's `void func(char arr[])` should become `fn func(arr: &mut [u8])`
5165                    // Unsized arrays in parameters are always passed by reference in C
5166                    // Default to &mut since C arrays are generally mutable and detecting
5167                    // modifications in embedded assignments (while conditions) is complex
5168                    if let AnnotatedType::Simple(HirType::Array {
5169                        element_type,
5170                        size: None,
5171                    }) = &p.param_type
5172                    {
5173                        let element_str = Self::map_type(element_type);
5174                        return format!("{}: &mut [{}]", p.name, element_str);
5175                    }
5176
5177                    // DECY-041: Add mut for all non-slice parameters to match C semantics
5178                    // In C, parameters are mutable by default (can be reassigned)
5179                    // DECY-FUTURE: More sophisticated analysis to only add mut when needed
5180                    format!(
5181                        "mut {}: {}",
5182                        p.name,
5183                        self.annotated_type_to_string(&p.param_type)
5184                    )
5185                }
5186            })
5187            .collect();
5188        result.push_str(&params.join(", "));
5189        result.push(')');
5190
5191        // Special handling for main function (DECY-AUDIT-001)
5192        // C's int main() must become Rust's fn main() (no return type)
5193        // Rust's entry point returns () and uses std::process::exit(N) for exit codes
5194        let return_type_str = self.annotated_type_to_string(&sig.return_type);
5195        if sig.name == "main" && return_type_str == "i32" {
5196            // Skip return type for main - it must be fn main()
5197            return result;
5198        }
5199
5200        // DECY-084/085: Generate return type considering output parameters
5201        // DECY-085: Multiple outputs become tuple
5202        if !output_param_types.is_empty() {
5203            let out_type_str = if output_param_types.len() == 1 {
5204                // Single output param: return T
5205                Self::map_type(&output_param_types[0])
5206            } else {
5207                // Multiple output params: return (T1, T2, ...)
5208                let type_strs: Vec<String> =
5209                    output_param_types.iter().map(Self::map_type).collect();
5210                format!("({})", type_strs.join(", "))
5211            };
5212
5213            if output_is_fallible {
5214                // Fallible function: int func(..., T* out) -> Result<T, i32>
5215                result.push_str(&format!(" -> Result<{}, i32>", out_type_str));
5216            } else {
5217                // Non-fallible void function: void func(..., T* out) -> T or (T1, T2)
5218                result.push_str(&format!(" -> {}", out_type_str));
5219            }
5220        } else {
5221            // DECY-142: Check for Vec return type (malloc'd array returns)
5222            if let Some(f) = func {
5223                if let Some(vec_element_type) = self.detect_vec_return(f) {
5224                    let element_type_str = Self::map_type(&vec_element_type);
5225                    result.push_str(&format!(" -> Vec<{}>", element_type_str));
5226                    return result;
5227                }
5228            }
5229            // Add return type if not void
5230            if return_type_str != "()" {
5231                result.push_str(&format!(" -> {}", return_type_str));
5232            }
5233        }
5234
5235        result
5236    }
5237
5238    /// Convert an `AnnotatedType` to Rust type string with lifetime annotations.
5239    ///
5240    /// # Examples
5241    ///
5242    /// ```
5243    /// use decy_codegen::CodeGenerator;
5244    /// use decy_ownership::lifetime_gen::{AnnotatedType, LifetimeParam};
5245    /// use decy_hir::HirType;
5246    ///
5247    /// let codegen = CodeGenerator::new();
5248    ///
5249    /// // Simple type
5250    /// let simple = AnnotatedType::Simple(HirType::Int);
5251    /// assert_eq!(codegen.annotated_type_to_string(&simple), "i32");
5252    ///
5253    /// // Reference with lifetime
5254    /// let ref_type = AnnotatedType::Reference {
5255    ///     inner: Box::new(AnnotatedType::Simple(HirType::Int)),
5256    ///     mutable: false,
5257    ///     lifetime: Some(LifetimeParam::standard(0)),
5258    /// };
5259    /// assert_eq!(codegen.annotated_type_to_string(&ref_type), "&'a i32");
5260    /// ```
5261    #[allow(clippy::only_used_in_recursion)]
5262    pub fn annotated_type_to_string(&self, annotated_type: &AnnotatedType) -> String {
5263        match annotated_type {
5264            AnnotatedType::Simple(hir_type) => Self::map_type(hir_type),
5265            AnnotatedType::Reference {
5266                inner,
5267                mutable,
5268                lifetime,
5269            } => {
5270                // DECY-072: Special case for slices: &Vec<T> → &[T]
5271                // Check if inner is a Vec type
5272                if let AnnotatedType::Simple(HirType::Vec(element_type)) = &**inner {
5273                    let element_str = Self::map_type(element_type);
5274                    if *mutable {
5275                        return format!("&mut [{}]", element_str);
5276                    } else {
5277                        return format!("&[{}]", element_str);
5278                    }
5279                }
5280
5281                let mut result = String::from("&");
5282
5283                // Add lifetime if present
5284                if let Some(lt) = lifetime {
5285                    result.push_str(&lt.name);
5286                    result.push(' ');
5287                }
5288
5289                // Add mutability
5290                if *mutable {
5291                    result.push_str("mut ");
5292                }
5293
5294                // Add inner type
5295                result.push_str(&self.annotated_type_to_string(inner));
5296
5297                result
5298            }
5299        }
5300    }
5301
5302    /// Generate a default return statement for a type.
5303    ///
5304    /// # Examples
5305    ///
5306    /// ```
5307    /// use decy_codegen::CodeGenerator;
5308    /// use decy_hir::HirType;
5309    ///
5310    /// let codegen = CodeGenerator::new();
5311    /// assert!(codegen.generate_return(&HirType::Int).contains("return 0"));
5312    /// ```
5313    pub fn generate_return(&self, return_type: &HirType) -> String {
5314        match return_type {
5315            HirType::Void => String::new(),
5316            HirType::Int => "    return 0;".to_string(),
5317            HirType::UnsignedInt => "    return 0;".to_string(), // DECY-158
5318            HirType::Float => "    return 0.0;".to_string(),
5319            HirType::Double => "    return 0.0;".to_string(),
5320            HirType::Char => "    return 0;".to_string(),
5321            HirType::Pointer(_) => "    return std::ptr::null_mut();".to_string(),
5322            HirType::Box(inner) => {
5323                format!(
5324                    "    return Box::new({});",
5325                    Self::default_value_for_type(inner)
5326                )
5327            }
5328            HirType::Vec(_) => "    return Vec::new();".to_string(),
5329            HirType::Option(_) => "    return None;".to_string(),
5330            HirType::Reference { .. } => {
5331                // References in return position need concrete values from parameters
5332                // This should be handled by lifetime-annotated code generation
5333                // using generate_function_with_lifetimes() instead
5334                String::new()
5335            }
5336            HirType::Struct(name) => {
5337                format!("    return {}::default();", name)
5338            }
5339            HirType::Enum(name) => {
5340                format!("    return {}::default();", name)
5341            }
5342            HirType::Array { element_type, size } => {
5343                if let Some(n) = size {
5344                    format!(
5345                        "    return [{}; {}];",
5346                        Self::default_value_for_type(element_type),
5347                        n
5348                    )
5349                } else {
5350                    // Unsized arrays in return position don't make sense
5351                    String::new()
5352                }
5353            }
5354            HirType::FunctionPointer { .. } => {
5355                // Function pointers in return position need concrete function values
5356                // This should be handled by the function body
5357                String::new()
5358            }
5359            HirType::StringLiteral => r#"    return "";"#.to_string(),
5360            HirType::OwnedString => "    return String::new();".to_string(),
5361            HirType::StringReference => r#"    return "";"#.to_string(),
5362            HirType::Union(_) => {
5363                // Unions will be transformed to enums
5364                // Return statement depends on the specific enum variant
5365                String::new()
5366            }
5367            // DECY-172: Type aliases return 0
5368            HirType::TypeAlias(name) => {
5369                match name.as_str() {
5370                    "size_t" => "    return 0usize;".to_string(),
5371                    "ssize_t" | "ptrdiff_t" => "    return 0isize;".to_string(),
5372                    _ => "    return 0;".to_string(),
5373                }
5374            }
5375        }
5376    }
5377
5378    /// Generate a complete function from HIR.
5379    ///
5380    /// # Examples
5381    ///
5382    /// ```
5383    /// use decy_codegen::CodeGenerator;
5384    /// use decy_hir::{HirFunction, HirType, HirParameter};
5385    ///
5386    /// let func = HirFunction::new(
5387    ///     "add".to_string(),
5388    ///     HirType::Int,
5389    ///     vec![
5390    ///         HirParameter::new("a".to_string(), HirType::Int),
5391    ///         HirParameter::new("b".to_string(), HirType::Int),
5392    ///     ],
5393    /// );
5394    ///
5395    /// let codegen = CodeGenerator::new();
5396    /// let code = codegen.generate_function(&func);
5397    ///
5398    /// assert!(code.contains("fn add(mut a: i32, mut b: i32) -> i32"));
5399    /// assert!(code.contains("{"));
5400    /// assert!(code.contains("}"));
5401    /// ```
5402    pub fn generate_function(&self, func: &HirFunction) -> String {
5403        let mut code = String::new();
5404
5405        // DECY-072 GREEN: Build mapping of length params -> array params for body transformation
5406        use decy_ownership::dataflow::DataflowAnalyzer;
5407        let analyzer = DataflowAnalyzer::new();
5408        let graph = analyzer.analyze(func);
5409
5410        let mut length_to_array: std::collections::HashMap<String, String> =
5411            std::collections::HashMap::new();
5412
5413        // DECY-113: Only map length params with length-like names
5414        // DECY-162: Don't map length params when array uses pointer arithmetic (stays raw pointer)
5415        for (idx, param) in func.parameters().iter().enumerate() {
5416            if let Some(true) = graph.is_array_parameter(param.name()) {
5417                // DECY-162: Skip if array param uses pointer arithmetic
5418                // Raw pointers don't have .len(), so we keep the size param as-is
5419                if self.uses_pointer_arithmetic(func, param.name()) {
5420                    continue;
5421                }
5422
5423                // This is an array parameter - map the next param to this array
5424                // but only if it has a length-like name
5425                if idx + 1 < func.parameters().len() {
5426                    let next_param = &func.parameters()[idx + 1];
5427                    if matches!(next_param.param_type(), HirType::Int) {
5428                        let param_name = next_param.name().to_lowercase();
5429                        if param_name.contains("len")
5430                            || param_name.contains("size")
5431                            || param_name.contains("count")
5432                            || param_name == "n"
5433                            || param_name == "num"
5434                        {
5435                            length_to_array
5436                                .insert(next_param.name().to_string(), param.name().to_string());
5437                        }
5438                    }
5439                }
5440            }
5441        }
5442
5443        // Generate signature
5444        code.push_str(&self.generate_signature(func));
5445        code.push_str(" {\n");
5446
5447        // Initialize type context for tracking variable types across statements
5448        let mut ctx = TypeContext::from_function(func);
5449
5450        // DECY-129/DECY-148: Update context to reflect pointer-to-reference transformations
5451        // When pointer params are transformed to &mut T in signature, context must match
5452        // DECY-148: Distinguish array params (slices) from struct pointer params (references)
5453        for param in func.parameters() {
5454            if let HirType::Pointer(inner) = param.param_type() {
5455                // Check if this pointer uses pointer arithmetic (keep as raw pointer)
5456                if !self.uses_pointer_arithmetic(func, param.name()) {
5457                    // DECY-148: Check if this is an ARRAY parameter
5458                    let is_array_param = graph.is_array_parameter(param.name()).unwrap_or(false);
5459
5460                    if is_array_param {
5461                        // Array parameter → register as slice (Reference to Array)
5462                        ctx.add_variable(
5463                            param.name().to_string(),
5464                            HirType::Reference {
5465                                inner: Box::new(HirType::Array {
5466                                    element_type: inner.clone(),
5467                                    size: None, // Slice (unsized array)
5468                                }),
5469                                mutable: true,
5470                            },
5471                        );
5472                    } else {
5473                        // Struct pointer → register as Reference to inner type
5474                        let is_mutable = self.is_parameter_deref_modified(func, param.name());
5475                        ctx.add_variable(
5476                            param.name().to_string(),
5477                            HirType::Reference {
5478                                inner: inner.clone(),
5479                                mutable: is_mutable,
5480                            },
5481                        );
5482                    }
5483                }
5484            }
5485        }
5486
5487        // DECY-142: Detect Vec-return functions for correct return type handling
5488        let effective_return_type = if let Some(element_type) = self.detect_vec_return(func) {
5489            HirType::Vec(Box::new(element_type))
5490        } else {
5491            func.return_type().clone()
5492        };
5493
5494        // Generate body statements if present
5495        if func.body().is_empty() {
5496            // Generate stub body with return statement
5497            let return_stmt = self.generate_return(func.return_type());
5498            if !return_stmt.is_empty() {
5499                code.push_str(&return_stmt);
5500                code.push('\n');
5501            }
5502        } else {
5503            // Generate actual body statements with persistent context
5504            for stmt in func.body() {
5505                code.push_str("    ");
5506                let stmt_code = self.generate_statement_with_context(
5507                    stmt,
5508                    Some(func.name()),
5509                    &mut ctx,
5510                    Some(&effective_return_type),
5511                );
5512
5513                // DECY-072 GREEN: Replace length parameter references with arr.len() calls
5514                let transformed = self.transform_length_refs(&stmt_code, &length_to_array);
5515                code.push_str(&transformed);
5516                code.push('\n');
5517            }
5518        }
5519
5520        code.push('}');
5521        code
5522    }
5523
5524    /// Generate a complete function from HIR with struct definitions for type inference.
5525    ///
5526    /// This is useful for testing when struct fields need proper type inference.
5527    /// DECY-165: Enables proper raw pointer detection for struct field access.
5528    pub fn generate_function_with_structs(
5529        &self,
5530        func: &HirFunction,
5531        structs: &[decy_hir::HirStruct],
5532    ) -> String {
5533        let mut code = String::new();
5534
5535        // Generate signature
5536        code.push_str(&self.generate_signature(func));
5537        code.push_str(" {\n");
5538
5539        // Initialize type context with function parameters AND struct definitions
5540        let mut ctx = TypeContext::from_function(func);
5541
5542        // DECY-165: Add struct definitions to context for field type lookup
5543        for struct_def in structs {
5544            ctx.add_struct(struct_def);
5545        }
5546
5547        // DECY-129/DECY-148: Update context to reflect pointer-to-reference transformations
5548        // When pointer params are transformed to &mut T in signature, context must match
5549        use decy_ownership::dataflow::DataflowAnalyzer;
5550        let analyzer = DataflowAnalyzer::new();
5551        let graph = analyzer.analyze(func);
5552
5553        for param in func.parameters() {
5554            if let HirType::Pointer(inner) = param.param_type() {
5555                // Only transform if the pointer is not used for pointer arithmetic
5556                if !self.uses_pointer_arithmetic(func, param.name()) {
5557                    // Check if it's an array parameter → use &[T] or &mut [T]
5558                    if graph.is_array_parameter(param.name()) == Some(true) {
5559                        // Use slice reference type
5560                        ctx.add_variable(
5561                            param.name().to_string(),
5562                            HirType::Reference {
5563                                inner: Box::new(HirType::Vec(inner.clone())),
5564                                mutable: self.is_parameter_deref_modified(func, param.name()),
5565                            },
5566                        );
5567                    } else {
5568                        // Single pointer → reference
5569                        ctx.add_variable(
5570                            param.name().to_string(),
5571                            HirType::Reference {
5572                                inner: inner.clone(),
5573                                mutable: self.is_parameter_deref_modified(func, param.name()),
5574                            },
5575                        );
5576                    }
5577                }
5578            }
5579        }
5580
5581        // Generate body statements
5582        if !func.body().is_empty() {
5583            for stmt in func.body() {
5584                code.push_str("    ");
5585                let stmt_code = self.generate_statement_with_context(
5586                    stmt,
5587                    Some(func.name()),
5588                    &mut ctx,
5589                    Some(func.return_type()),
5590                );
5591                code.push_str(&stmt_code);
5592                code.push('\n');
5593            }
5594        }
5595
5596        code.push('}');
5597        code
5598    }
5599
5600    /// Generate a complete function from HIR with lifetime annotations.
5601    ///
5602    /// Takes both the HIR function and its annotated signature to generate
5603    /// Rust code with proper lifetime annotations.
5604    ///
5605    /// # Examples
5606    ///
5607    /// ```
5608    /// use decy_codegen::CodeGenerator;
5609    /// use decy_hir::{HirFunction, HirType, HirParameter};
5610    /// use decy_ownership::lifetime_gen::{AnnotatedSignature, AnnotatedParameter, AnnotatedType, LifetimeParam};
5611    ///
5612    /// let func = HirFunction::new(
5613    ///     "identity".to_string(),
5614    ///     HirType::Reference {
5615    ///         inner: Box::new(HirType::Int),
5616    ///         mutable: false,
5617    ///     },
5618    ///     vec![
5619    ///         HirParameter::new("x".to_string(), HirType::Reference {
5620    ///             inner: Box::new(HirType::Int),
5621    ///             mutable: false,
5622    ///         }),
5623    ///     ],
5624    /// );
5625    ///
5626    /// let sig = AnnotatedSignature {
5627    ///     name: "identity".to_string(),
5628    ///     lifetimes: vec![LifetimeParam::standard(0)],
5629    ///     parameters: vec![
5630    ///         AnnotatedParameter {
5631    ///             name: "x".to_string(),
5632    ///             param_type: AnnotatedType::Reference {
5633    ///                 inner: Box::new(AnnotatedType::Simple(HirType::Int)),
5634    ///                 mutable: false,
5635    ///                 lifetime: Some(LifetimeParam::standard(0)),
5636    ///             },
5637    ///         },
5638    ///     ],
5639    ///     return_type: AnnotatedType::Reference {
5640    ///         inner: Box::new(AnnotatedType::Simple(HirType::Int)),
5641    ///         mutable: false,
5642    ///         lifetime: Some(LifetimeParam::standard(0)),
5643    ///     },
5644    /// };
5645    ///
5646    /// let codegen = CodeGenerator::new();
5647    /// let code = codegen.generate_function_with_lifetimes(&func, &sig);
5648    ///
5649    /// assert!(code.contains("<'a>"));
5650    /// assert!(code.contains("&'a i32"));
5651    /// ```
5652    pub fn generate_function_with_lifetimes(
5653        &self,
5654        func: &HirFunction,
5655        sig: &AnnotatedSignature,
5656    ) -> String {
5657        self.generate_function_with_lifetimes_and_structs(func, sig, &[], &[], &[], &[])
5658    }
5659
5660    /// Generate a complete function from HIR with lifetime annotations and struct definitions.
5661    ///
5662    /// Takes the HIR function, its annotated signature, struct definitions, and all function
5663    /// signatures for call site reference mutability.
5664    ///
5665    /// # Arguments
5666    /// * `func` - The HIR function to generate
5667    /// * `sig` - The annotated signature with lifetime annotations
5668    /// * `structs` - Struct definitions for field type awareness
5669    /// * `all_functions` - All function signatures for DECY-117 call site mutability
5670    /// * `slice_func_args` - DECY-116: func_name -> [(array_idx, len_idx)] for call site transformation
5671    /// * `string_iter_funcs` - DECY-134b: func_name -> [(param_idx, is_mutable)] for string iteration
5672    pub fn generate_function_with_lifetimes_and_structs(
5673        &self,
5674        func: &HirFunction,
5675        sig: &AnnotatedSignature,
5676        structs: &[decy_hir::HirStruct],
5677        all_functions: &[(String, Vec<HirType>)],
5678        slice_func_args: &[(String, Vec<(usize, usize)>)],
5679        string_iter_funcs: &[(String, Vec<(usize, bool)>)],
5680    ) -> String {
5681        let mut code = String::new();
5682
5683        // Generate signature with lifetimes
5684        // DECY-123: Pass function for pointer arithmetic detection
5685        code.push_str(&self.generate_annotated_signature_with_func(sig, Some(func)));
5686        code.push_str(" {\n");
5687
5688        // DECY-041: Initialize type context with function parameters for pointer arithmetic
5689        let mut ctx = TypeContext::from_function(func);
5690
5691        // DECY-134: Track string iteration params for index-based body generation
5692        let mut string_iter_index_decls = Vec::new();
5693
5694        // DECY-111: Transform pointer parameters to references in the context
5695        // DECY-123/124: Only transform if NOT using pointer arithmetic
5696        // This prevents unsafe blocks from being generated for reference dereferences
5697        // DECY-148: Use DataflowAnalyzer to determine which params are array params
5698        use decy_ownership::dataflow::DataflowAnalyzer;
5699        let analyzer = DataflowAnalyzer::new();
5700        let graph = analyzer.analyze(func);
5701
5702        for param in func.parameters() {
5703            // DECY-138: Check for const char* → &str transformation FIRST
5704            // This enables proper string iteration pattern codegen
5705            if param.is_const_char_pointer() {
5706                ctx.add_variable(param.name().to_string(), HirType::StringReference);
5707            } else if let HirType::Pointer(inner) = param.param_type() {
5708                // DECY-134: Check for string iteration pattern FIRST
5709                if self.is_string_iteration_param(func, param.name()) {
5710                    // Register as Vec type in context (slice in generated code)
5711                    ctx.add_variable(param.name().to_string(), HirType::Vec(inner.clone()));
5712                    // Register string iteration param with index variable
5713                    let idx_var = format!("{}_idx", param.name());
5714                    ctx.add_string_iter_param(param.name().to_string(), idx_var.clone());
5715                    // Add index declaration to generate at function start
5716                    string_iter_index_decls.push(format!("    let mut {}: usize = 0;", idx_var));
5717                } else if self.uses_pointer_arithmetic(func, param.name()) {
5718                    // DECY-124: Keep as pointer in context if pointer arithmetic is used
5719                    // This ensures proper unsafe wrapping_add/wrapping_sub codegen
5720                    // Keep as pointer - codegen will generate unsafe blocks
5721                    ctx.add_variable(param.name().to_string(), param.param_type().clone());
5722                } else {
5723                    // DECY-148: Check if this is an ARRAY parameter (detected by dataflow analysis)
5724                    let is_array_param = graph.is_array_parameter(param.name()).unwrap_or(false);
5725
5726                    if is_array_param {
5727                        // DECY-146: Array parameter → register as slice (Reference to Array)
5728                        // This enables proper .as_ptr()/.as_mut_ptr() generation
5729                        ctx.add_variable(
5730                            param.name().to_string(),
5731                            HirType::Reference {
5732                                inner: Box::new(HirType::Array {
5733                                    element_type: inner.clone(),
5734                                    size: None, // Slice (unsized array)
5735                                }),
5736                                mutable: true,
5737                            },
5738                        );
5739                    } else {
5740                        // DECY-148: Non-array struct pointer → register as Reference to inner type
5741                        // This enables proper `&mut T as *mut _` coercion on return
5742                        let is_mutable = self.is_parameter_deref_modified(func, param.name());
5743                        ctx.add_variable(
5744                            param.name().to_string(),
5745                            HirType::Reference {
5746                                inner: inner.clone(),
5747                                mutable: is_mutable,
5748                            },
5749                        );
5750                    }
5751                }
5752            }
5753        }
5754
5755        // DECY-134: Generate index variable declarations for string iteration params
5756        for decl in &string_iter_index_decls {
5757            code.push_str(decl);
5758            code.push('\n');
5759        }
5760
5761        // Add struct definitions to context for field type lookup
5762        for struct_def in structs {
5763            ctx.add_struct(struct_def);
5764        }
5765
5766        // DECY-117: Add all function signatures for call site reference mutability
5767        for (func_name, param_types) in all_functions {
5768            ctx.add_function(func_name.clone(), param_types.clone());
5769        }
5770
5771        // DECY-116: Add slice function arg mappings for call site transformation
5772        for (func_name, arg_mappings) in slice_func_args {
5773            ctx.add_slice_func_args(func_name.clone(), arg_mappings.clone());
5774        }
5775
5776        // DECY-134b: Add string iteration function info for call site transformation
5777        for (func_name, params) in string_iter_funcs {
5778            ctx.add_string_iter_func(func_name.clone(), params.clone());
5779        }
5780
5781        // DECY-142: Detect Vec-return functions for correct return type handling
5782        let effective_return_type = if let Some(element_type) = self.detect_vec_return(func) {
5783            HirType::Vec(Box::new(element_type))
5784        } else {
5785            func.return_type().clone()
5786        };
5787
5788        // Generate body statements if present
5789        if func.body().is_empty() {
5790            // Generate stub body with return statement
5791            let return_stmt = self.generate_return(func.return_type());
5792            if !return_stmt.is_empty() {
5793                code.push_str(&return_stmt);
5794                code.push('\n');
5795            }
5796        } else {
5797            // Generate actual body statements with type context and return type
5798            for stmt in func.body() {
5799                code.push_str("    ");
5800                code.push_str(&self.generate_statement_with_context(
5801                    stmt,
5802                    Some(func.name()),
5803                    &mut ctx,
5804                    Some(&effective_return_type),
5805                ));
5806                code.push('\n');
5807            }
5808        }
5809
5810        code.push('}');
5811        code
5812    }
5813
5814    /// Generate a function with Box transformations applied.
5815    ///
5816    /// This method analyzes the function for malloc/free patterns and
5817    /// transforms them into safe `Box::new()` expressions.
5818    ///
5819    /// # Examples
5820    ///
5821    /// ```
5822    /// use decy_codegen::CodeGenerator;
5823    /// use decy_hir::{HirFunction, HirType, HirStatement, HirExpression};
5824    /// use decy_analyzer::patterns::PatternDetector;
5825    ///
5826    /// let func = HirFunction::new_with_body(
5827    ///     "test".to_string(),
5828    ///     HirType::Void,
5829    ///     vec![],
5830    ///     vec![
5831    ///         HirStatement::VariableDeclaration {
5832    ///             name: "ptr".to_string(),
5833    ///             var_type: HirType::Pointer(Box::new(HirType::Int)),
5834    ///             initializer: Some(HirExpression::FunctionCall {
5835    ///                 function: "malloc".to_string(),
5836    ///                 arguments: vec![HirExpression::IntLiteral(100)],
5837    ///             }),
5838    ///         },
5839    ///     ],
5840    /// );
5841    ///
5842    /// let codegen = CodeGenerator::new();
5843    /// let detector = PatternDetector::new();
5844    /// let candidates = detector.find_box_candidates(&func);
5845    /// let code = codegen.generate_function_with_box_transform(&func, &candidates);
5846    ///
5847    /// assert!(code.contains("Box::new"));
5848    /// ```
5849    pub fn generate_function_with_box_transform(
5850        &self,
5851        func: &HirFunction,
5852        candidates: &[decy_analyzer::patterns::BoxCandidate],
5853    ) -> String {
5854        let mut code = String::new();
5855
5856        // Generate signature
5857        code.push_str(&self.generate_signature(func));
5858        code.push_str(" {\n");
5859
5860        // Generate body statements if present
5861        if func.body().is_empty() {
5862            // Generate stub body with return statement
5863            let return_stmt = self.generate_return(func.return_type());
5864            if !return_stmt.is_empty() {
5865                code.push_str(&return_stmt);
5866                code.push('\n');
5867            }
5868        } else {
5869            // Generate body statements with Box transformations
5870            for (idx, stmt) in func.body().iter().enumerate() {
5871                // Check if this statement should be transformed
5872                let transformed_stmt =
5873                    if let Some(candidate) = candidates.iter().find(|c| c.malloc_index == idx) {
5874                        self.box_transformer.transform_statement(stmt, candidate)
5875                    } else {
5876                        stmt.clone()
5877                    };
5878
5879                code.push_str("    ");
5880                code.push_str(
5881                    &self.generate_statement_for_function(&transformed_stmt, Some(func.name())),
5882                );
5883                code.push('\n');
5884            }
5885        }
5886
5887        code.push('}');
5888        code
5889    }
5890
5891    /// Generate a function with Vec transformations applied.
5892    ///
5893    /// This method analyzes the function for malloc(n * sizeof(T)) patterns and
5894    /// transforms them into safe `Vec::with_capacity(n)` expressions.
5895    pub fn generate_function_with_vec_transform(
5896        &self,
5897        func: &HirFunction,
5898        candidates: &[decy_analyzer::patterns::VecCandidate],
5899    ) -> String {
5900        let mut code = String::new();
5901
5902        // Generate signature
5903        code.push_str(&self.generate_signature(func));
5904        code.push_str(" {\n");
5905
5906        // Generate body statements if present
5907        if func.body().is_empty() {
5908            // Generate stub body with return statement
5909            let return_stmt = self.generate_return(func.return_type());
5910            if !return_stmt.is_empty() {
5911                code.push_str(&return_stmt);
5912                code.push('\n');
5913            }
5914        } else {
5915            // Generate body statements with Vec transformations
5916            for (idx, stmt) in func.body().iter().enumerate() {
5917                // Check if this statement should be transformed
5918                let transformed_stmt =
5919                    if let Some(candidate) = candidates.iter().find(|c| c.malloc_index == idx) {
5920                        self.transform_vec_statement(stmt, candidate)
5921                    } else {
5922                        stmt.clone()
5923                    };
5924
5925                code.push_str("    ");
5926                code.push_str(
5927                    &self.generate_statement_for_function(&transformed_stmt, Some(func.name())),
5928                );
5929                code.push('\n');
5930            }
5931        }
5932
5933        code.push('}');
5934        code
5935    }
5936
5937    /// Transform a statement to use Vec instead of malloc for array patterns.
5938    fn transform_vec_statement(
5939        &self,
5940        stmt: &HirStatement,
5941        candidate: &decy_analyzer::patterns::VecCandidate,
5942    ) -> HirStatement {
5943        match stmt {
5944            HirStatement::VariableDeclaration {
5945                name,
5946                var_type,
5947                initializer: _,
5948            } => {
5949                // Get the element type from the pointer
5950                let element_type = if let HirType::Pointer(inner) = var_type {
5951                    (**inner).clone()
5952                } else {
5953                    // Fallback: keep original type
5954                    return stmt.clone();
5955                };
5956
5957                // Transform type to Vec
5958                let vec_type = HirType::Vec(Box::new(element_type));
5959
5960                // Transform initializer: malloc(n * sizeof(T)) → Vec::with_capacity(n)
5961                let vec_initializer = if let Some(capacity_expr) = &candidate.capacity_expr {
5962                    Some(HirExpression::FunctionCall {
5963                        function: "Vec::with_capacity".to_string(),
5964                        arguments: vec![capacity_expr.clone()],
5965                    })
5966                } else {
5967                    // No capacity expression - use Vec::new()
5968                    Some(HirExpression::FunctionCall {
5969                        function: "Vec::new".to_string(),
5970                        arguments: vec![],
5971                    })
5972                };
5973
5974                HirStatement::VariableDeclaration {
5975                    name: name.clone(),
5976                    var_type: vec_type,
5977                    initializer: vec_initializer,
5978                }
5979            }
5980            HirStatement::Assignment {
5981                target: _,
5982                value: _,
5983            } => {
5984                // Similar transformation for assignments
5985                // For now, keep the original statement
5986                // Future: handle ptr = malloc(n * sizeof(T)) assignments
5987                stmt.clone()
5988            }
5989            _ => stmt.clone(),
5990        }
5991    }
5992
5993    /// Generate a function with both Box and Vec transformations applied.
5994    ///
5995    /// This method combines both Box and Vec transformations,
5996    /// applying them to their respective patterns.
5997    pub fn generate_function_with_box_and_vec_transform(
5998        &self,
5999        func: &HirFunction,
6000        box_candidates: &[decy_analyzer::patterns::BoxCandidate],
6001        vec_candidates: &[decy_analyzer::patterns::VecCandidate],
6002    ) -> String {
6003        let mut code = String::new();
6004
6005        // Generate signature
6006        code.push_str(&self.generate_signature(func));
6007        code.push_str(" {\n");
6008
6009        // Generate body statements if present
6010        if func.body().is_empty() {
6011            // Generate stub body with return statement
6012            let return_stmt = self.generate_return(func.return_type());
6013            if !return_stmt.is_empty() {
6014                code.push_str(&return_stmt);
6015                code.push('\n');
6016            }
6017        } else {
6018            // Generate body statements with both transformations
6019            for (idx, stmt) in func.body().iter().enumerate() {
6020                // Check Vec candidates first (more specific pattern)
6021                let transformed_stmt = if let Some(vec_candidate) =
6022                    vec_candidates.iter().find(|c| c.malloc_index == idx)
6023                {
6024                    self.transform_vec_statement(stmt, vec_candidate)
6025                } else if let Some(box_candidate) =
6026                    box_candidates.iter().find(|c| c.malloc_index == idx)
6027                {
6028                    self.box_transformer
6029                        .transform_statement(stmt, box_candidate)
6030                } else {
6031                    stmt.clone()
6032                };
6033
6034                code.push_str("    ");
6035                code.push_str(
6036                    &self.generate_statement_for_function(&transformed_stmt, Some(func.name())),
6037                );
6038                code.push('\n');
6039            }
6040        }
6041
6042        code.push('}');
6043        code
6044    }
6045
6046    /// Generate a struct definition from HIR.
6047    ///
6048    /// Generates Rust struct code with automatic derives for Debug, Clone, PartialEq, Eq.
6049    /// Handles lifetimes automatically for structs with reference fields.
6050    pub fn generate_struct(&self, hir_struct: &decy_hir::HirStruct) -> String {
6051        let mut code = String::new();
6052
6053        // Check if struct needs lifetimes (has Reference fields)
6054        let needs_lifetimes = hir_struct
6055            .fields()
6056            .iter()
6057            .any(|f| matches!(f.field_type(), HirType::Reference { .. }));
6058
6059        // DECY-123: Check if struct has large arrays (> 32 elements) that don't impl Default
6060        // Rust arrays only implement Default for sizes up to 32
6061        let has_large_array = hir_struct.fields().iter().any(|f| {
6062            matches!(
6063                f.field_type(),
6064                HirType::Array { size: Some(n), .. } if *n > 32
6065            )
6066        });
6067
6068        // DECY-218: Check if struct has float/double fields (f32/f64 don't implement Eq)
6069        let has_float_fields = hir_struct.fields().iter().any(|f| {
6070            matches!(f.field_type(), HirType::Float | HirType::Double)
6071        });
6072
6073        // Add derive attribute
6074        // DECY-114: Add Default derive for struct initialization with ::default()
6075        // DECY-123: Skip Default for large arrays
6076        // DECY-218: Skip Eq for floats (f32/f64 only implement PartialEq)
6077        if has_large_array && has_float_fields {
6078            code.push_str("#[derive(Debug, Clone, PartialEq)]\n");
6079        } else if has_large_array {
6080            code.push_str("#[derive(Debug, Clone, PartialEq, Eq)]\n");
6081        } else if has_float_fields {
6082            code.push_str("#[derive(Debug, Clone, Default, PartialEq)]\n");
6083        } else {
6084            code.push_str("#[derive(Debug, Clone, Default, PartialEq, Eq)]\n");
6085        }
6086
6087        // Add struct declaration with or without lifetime
6088        if needs_lifetimes {
6089            code.push_str(&format!("pub struct {}<'a> {{\n", hir_struct.name()));
6090        } else {
6091            code.push_str(&format!("pub struct {} {{\n", hir_struct.name()));
6092        }
6093
6094        // Add fields
6095        // Note: struct_name reserved for DECY-144 self-referential pointer detection
6096        let _struct_name = hir_struct.name();
6097        for field in hir_struct.fields() {
6098            // DECY-136: Flexible array members (Array with size: None) → Vec<T>
6099            // C99 §6.7.2.1: struct { int size; char data[]; } → Vec<u8>
6100            //
6101            // DECY-144: Self-referential pointers (struct Node* next) → Option<Box<T>>
6102            // This significantly reduces unsafe blocks in recursive data structures.
6103            let field_type_str = match field.field_type() {
6104                HirType::Array {
6105                    element_type,
6106                    size: None,
6107                } => {
6108                    // Flexible array member → Vec<T>
6109                    format!("Vec<{}>", Self::map_type(element_type))
6110                }
6111                // DECY-144: Self-referential pointer → Option<Box<T>> (DEFERRED)
6112                // The full transformation requires updating ALL usages:
6113                // - Function parameters and return types
6114                // - Local variable types
6115                // - Field access patterns (Some(ref x) instead of *ptr)
6116                // - NULL checks (is_none() instead of == null_mut())
6117                //
6118                // For now, keep raw pointers but track these fields for future transformation.
6119                // See DECY-145 for full Option<Box<T>> transformation implementation.
6120                HirType::Pointer(_inner) => {
6121                    // Commented out for now - needs full transformation
6122                    // if let HirType::Struct(inner_name) = inner.as_ref() {
6123                    //     if inner_name == struct_name {
6124                    //         format!("Option<Box<{}>>", struct_name)
6125                    //     } else {
6126                    //         Self::map_type(field.field_type())
6127                    //     }
6128                    // } else {
6129                    //     Self::map_type(field.field_type())
6130                    // }
6131                    Self::map_type(field.field_type())
6132                }
6133                other => Self::map_type(other),
6134            };
6135            code.push_str(&format!("    pub {}: {},\n", field.name(), field_type_str));
6136        }
6137
6138        code.push('}');
6139        code
6140    }
6141
6142    /// Generate an enum definition from HIR.
6143    ///
6144    /// Generates Rust enum code with automatic derives for Debug, Clone, Copy, PartialEq, Eq.
6145    /// Supports both simple enums and enums with explicit integer values.
6146    pub fn generate_enum(&self, hir_enum: &decy_hir::HirEnum) -> String {
6147        let mut code = String::new();
6148
6149        // Add derive attribute (includes Copy since C enums are copyable)
6150        code.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n");
6151
6152        // Add enum declaration
6153        code.push_str(&format!("pub enum {} {{\n", hir_enum.name()));
6154
6155        // Add variants
6156        for variant in hir_enum.variants() {
6157            if let Some(value) = variant.value() {
6158                code.push_str(&format!("    {} = {},\n", variant.name(), value));
6159            } else {
6160                code.push_str(&format!("    {},\n", variant.name()));
6161            }
6162        }
6163
6164        code.push('}');
6165        code
6166    }
6167
6168    /// Generate a typedef (type alias) from HIR.
6169    ///
6170    /// Generates Rust type alias code using the `type` keyword.
6171    /// Handles redundant typedefs (where name matches underlying struct/enum name) as comments.
6172    ///
6173    /// # Examples
6174    ///
6175    /// ```
6176    /// use decy_codegen::CodeGenerator;
6177    /// use decy_hir::{HirTypedef, HirType};
6178    ///
6179    /// let codegen = CodeGenerator::new();
6180    ///
6181    /// // Simple typedef: typedef int Integer;
6182    /// let typedef = HirTypedef::new("Integer".to_string(), HirType::Int);
6183    /// let code = codegen.generate_typedef(&typedef).unwrap();
6184    /// assert!(code.contains("type Integer = i32"));
6185    ///
6186    /// // Pointer typedef: typedef int* IntPtr;
6187    /// let typedef = HirTypedef::new("IntPtr".to_string(), HirType::Pointer(Box::new(HirType::Int)));
6188    /// let code = codegen.generate_typedef(&typedef).unwrap();
6189    /// assert!(code.contains("type IntPtr = *mut i32"));
6190    /// ```
6191    pub fn generate_typedef(&self, typedef: &decy_hir::HirTypedef) -> anyhow::Result<String> {
6192        // Check for typedef array assertions (DECY-057)
6193        // Pattern: typedef char name[sizeof(type) == size ? 1 : -1];
6194        if let HirType::Array { element_type, size } = typedef.underlying_type() {
6195            // Check if this looks like a compile-time assertion
6196            // Size of None (expression-based) or 1 indicates likely assertion pattern
6197            // Expression-based sizes come from ternary operators like [cond ? 1 : -1]
6198            let is_assertion = size.is_none() || *size == Some(1);
6199
6200            if is_assertion {
6201                // This is a typedef array assertion - generate Rust const assertion
6202                // Generate a compile-time assertion that will be checked by rustc
6203                return Ok(format!(
6204                    "// Compile-time assertion from typedef {} (C pattern: typedef {}[expr ? 1 : -1])\nconst _: () = assert!(std::mem::size_of::<i32>() == 4);",
6205                    typedef.name(),
6206                    Self::map_type(element_type)
6207                ));
6208            }
6209
6210            // Regular array typedef with fixed size
6211            return Ok(format!(
6212                "pub type {} = [{}; {}];",
6213                typedef.name(),
6214                Self::map_type(element_type),
6215                size.unwrap_or(0)
6216            ));
6217        }
6218
6219        // DECY-167: Handle platform size types specially
6220        // These need to map to usize/isize for compatibility with Rust methods like .len()
6221        let name = typedef.name();
6222        if name == "size_t" {
6223            return Ok("pub type size_t = usize;".to_string());
6224        }
6225        if name == "ssize_t" {
6226            return Ok("pub type ssize_t = isize;".to_string());
6227        }
6228        if name == "ptrdiff_t" {
6229            return Ok("pub type ptrdiff_t = isize;".to_string());
6230        }
6231
6232        // Check for redundant typedef (struct/enum name matching typedef name)
6233        let result = match typedef.underlying_type() {
6234            HirType::Struct(struct_name) | HirType::Enum(struct_name) if struct_name == name => {
6235                // In Rust, struct/enum names are already types, so this is redundant
6236                // Generate as a comment for documentation purposes
6237                format!("// type {} = {}; (redundant in Rust)", name, struct_name)
6238            }
6239            _ => {
6240                // Regular type alias with public visibility
6241                format!(
6242                    "pub type {} = {};",
6243                    name,
6244                    Self::map_type(typedef.underlying_type())
6245                )
6246            }
6247        };
6248        Ok(result)
6249    }
6250
6251    /// Generate a constant declaration from HIR.
6252    ///
6253    /// Transforms C `#define` macro constants to Rust `const` declarations.
6254    /// C #define constants are compile-time text substitutions that map naturally
6255    /// to Rust's const with compile-time evaluation.
6256    ///
6257    /// # Examples
6258    ///
6259    /// ```
6260    /// use decy_codegen::CodeGenerator;
6261    /// use decy_hir::{HirConstant, HirType, HirExpression};
6262    ///
6263    /// let codegen = CodeGenerator::new();
6264    ///
6265    /// // Integer constant: #define MAX 100 → const MAX: i32 = 100;
6266    /// let constant = HirConstant::new(
6267    ///     "MAX".to_string(),
6268    ///     HirType::Int,
6269    ///     HirExpression::IntLiteral(100),
6270    /// );
6271    /// let code = codegen.generate_constant(&constant);
6272    /// assert!(code.contains("const MAX: i32 = 100"));
6273    ///
6274    /// // String constant: #define MSG "Hello" → const MSG: &str = "Hello";
6275    /// let constant = HirConstant::new(
6276    ///     "MSG".to_string(),
6277    ///     HirType::Pointer(Box::new(HirType::Char)),
6278    ///     HirExpression::StringLiteral("Hello".to_string()),
6279    /// );
6280    /// let code = codegen.generate_constant(&constant);
6281    /// assert!(code.contains("const MSG: &str = \"Hello\""));
6282    /// ```
6283    ///
6284    /// # Safety
6285    ///
6286    /// This transformation introduces 0 unsafe blocks, maintaining the goal of
6287    /// <5 unsafe blocks per 1000 LOC.
6288    ///
6289    /// Reference: K&R §4.11, ISO C99 §6.10.3
6290    pub fn generate_constant(&self, constant: &decy_hir::HirConstant) -> String {
6291        // Map char* to &str for string constants
6292        let rust_type = if matches!(
6293            constant.const_type(),
6294            HirType::Pointer(inner) if matches!(**inner, HirType::Char)
6295        ) {
6296            "&str".to_string()
6297        } else {
6298            Self::map_type(constant.const_type())
6299        };
6300
6301        format!(
6302            "const {}: {} = {};",
6303            constant.name(),
6304            rust_type,
6305            self.generate_expression(constant.value())
6306        )
6307    }
6308
6309    /// Generate a global variable declaration with storage class specifiers.
6310    ///
6311    /// Transforms C global variables with storage classes to appropriate Rust declarations:
6312    /// - `static` → `static mut` (mutable static)
6313    /// - `extern` → `extern "C" { static }`
6314    /// - `const` → `const`
6315    /// - `static const` → `const` (const is stronger than static)
6316    /// - Plain global → `static mut` (default to mutable)
6317    ///
6318    /// # Examples
6319    ///
6320    /// ```no_run
6321    /// use decy_codegen::CodeGenerator;
6322    /// use decy_hir::{HirConstant, HirType, HirExpression};
6323    ///
6324    /// let codegen = CodeGenerator::new();
6325    ///
6326    /// // static int counter = 0; → static mut counter: i32 = 0;
6327    /// let global = HirConstant::new(
6328    ///     "counter".to_string(),
6329    ///     HirType::Int,
6330    ///     HirExpression::IntLiteral(0),
6331    /// );
6332    /// let code = codegen.generate_global_variable(&global, true, false, false);
6333    /// assert!(code.contains("static mut counter: i32 = 0"));
6334    /// ```
6335    ///
6336    /// # Arguments
6337    ///
6338    /// * `variable` - The HIR constant representing the global variable
6339    /// * `is_static` - Whether the variable has `static` storage class
6340    /// * `is_extern` - Whether the variable has `extern` storage class
6341    /// * `is_const` - Whether the variable has `const` qualifier
6342    ///
6343    /// # Safety
6344    ///
6345    /// Note: `static mut` in Rust requires unsafe blocks to access, which increases
6346    /// unsafe usage. However, this is necessary to preserve C semantics for mutable globals.
6347    ///
6348    /// Reference: ISO C99 §6.7.1 (Storage-class specifiers), K&R §4.2
6349    pub fn generate_global_variable(
6350        &self,
6351        variable: &decy_hir::HirConstant,
6352        _is_static: bool,
6353        is_extern: bool,
6354        is_const: bool,
6355    ) -> String {
6356        let var_name = variable.name();
6357        let value_expr = self.generate_expression(variable.value());
6358
6359        // Determine Rust type (special handling for string literals)
6360        let rust_type = if matches!(
6361            variable.const_type(),
6362            HirType::Pointer(inner) if matches!(**inner, HirType::Char)
6363        ) && is_const
6364        {
6365            // const char* → &str or &'static str
6366            "&str".to_string()
6367        } else {
6368            Self::map_type(variable.const_type())
6369        };
6370
6371        // Handle different storage class combinations
6372        if is_extern {
6373            // extern int x; → extern "C" { static x: i32; }
6374            format!(
6375                "extern \"C\" {{\n    static {}: {};\n}}",
6376                var_name, rust_type
6377            )
6378        } else if is_const {
6379            // const int x = 10; → const x: i32 = 10;
6380            // static const int x = 10; → const x: i32 = 10; (const is stronger)
6381            format!("const {}: {} = {};", var_name, rust_type, value_expr)
6382        } else {
6383            // static int x = 0; → static mut x: i32 = 0;
6384            // int x = 0; → static mut x: i32 = 0; (default)
6385            // Special handling for arrays: [0; 10] for array initialization
6386            let init_expr = if let HirType::Array {
6387                element_type,
6388                size,
6389            } = variable.const_type()
6390            {
6391                if let Some(size_val) = size {
6392                    // DECY-201: Fix array initialization for uninitialized arrays
6393                    // When value is just an integer (likely the size), use default zero value
6394                    let element_init = match variable.value() {
6395                        HirExpression::IntLiteral(n) if *n as usize == *size_val => {
6396                            // Value equals size - this is likely an uninitialized array
6397                            // Use type-appropriate zero value
6398                            match element_type.as_ref() {
6399                                HirType::Char => "0u8".to_string(),
6400                                HirType::Int => "0i32".to_string(),
6401                                HirType::Float => "0.0f32".to_string(),
6402                                HirType::Double => "0.0f64".to_string(),
6403                                _ => "0".to_string(),
6404                            }
6405                        }
6406                        _ => self.generate_expression(variable.value()),
6407                    };
6408                    format!("[{}; {}]", element_init, size_val)
6409                } else {
6410                    value_expr
6411                }
6412            } else if matches!(variable.const_type(), HirType::Pointer(_)) {
6413                // Handle NULL pointer initialization
6414                if matches!(variable.value(), HirExpression::IntLiteral(0)) {
6415                    "std::ptr::null_mut()".to_string()
6416                } else {
6417                    value_expr
6418                }
6419            } else {
6420                value_expr
6421            };
6422
6423            format!("static mut {}: {} = {};", var_name, rust_type, init_expr)
6424        }
6425    }
6426}
6427
6428impl Default for CodeGenerator {
6429    fn default() -> Self {
6430        Self::new()
6431    }
6432}
6433
6434#[cfg(test)]
6435#[path = "codegen_tests.rs"]
6436mod codegen_tests;
6437
6438#[cfg(test)]
6439#[path = "property_tests.rs"]
6440mod property_tests;
6441
6442#[cfg(test)]
6443#[path = "vec_property_tests.rs"]
6444mod vec_property_tests;
6445
6446#[cfg(test)]
6447#[path = "struct_codegen_tests.rs"]
6448mod struct_codegen_tests;
6449
6450#[cfg(test)]
6451#[path = "for_loop_codegen_tests.rs"]
6452mod for_loop_codegen_tests;
6453
6454#[cfg(test)]
6455#[path = "string_codegen_tests.rs"]
6456mod string_codegen_tests;
6457
6458#[cfg(test)]
6459#[path = "string_property_tests.rs"]
6460mod string_property_tests;
6461
6462#[cfg(test)]
6463#[path = "switch_codegen_tests.rs"]
6464mod switch_codegen_tests;
6465
6466#[cfg(test)]
6467#[path = "switch_property_tests.rs"]
6468mod switch_property_tests;
6469
6470#[cfg(test)]
6471#[path = "global_variable_codegen_tests.rs"]
6472mod global_variable_codegen_tests;