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