Skip to main content

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