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(¯o_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(¶ms.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(¶ms.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(<.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;