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}
49
50impl TypeContext {
51 fn new() -> Self {
52 Self {
53 variables: HashMap::new(),
54 structs: HashMap::new(),
55 }
56 }
57
58 fn from_function(func: &HirFunction) -> Self {
59 let mut ctx = Self::new();
60 // Add parameters to context
61 for param in func.parameters() {
62 ctx.variables
63 .insert(param.name().to_string(), param.param_type().clone());
64 }
65 ctx
66 }
67
68 fn add_variable(&mut self, name: String, var_type: HirType) {
69 self.variables.insert(name, var_type);
70 }
71
72 fn add_struct(&mut self, struct_def: &decy_hir::HirStruct) {
73 let fields: Vec<(String, HirType)> = struct_def
74 .fields()
75 .iter()
76 .map(|f| (f.name().to_string(), f.field_type().clone()))
77 .collect();
78 self.structs.insert(struct_def.name().to_string(), fields);
79 }
80
81 fn get_type(&self, name: &str) -> Option<&HirType> {
82 self.variables.get(name)
83 }
84
85 fn get_field_type(&self, object_expr: &HirExpression, field_name: &str) -> Option<HirType> {
86 // Get the type of the object expression
87 let object_type = match object_expr {
88 HirExpression::Variable(var_name) => self.get_type(var_name)?,
89 _ => return None,
90 };
91
92 // Extract the struct name from the type
93 let struct_name = match object_type {
94 HirType::Struct(name) => name,
95 HirType::Pointer(inner) => {
96 // If it's a pointer to a struct, dereference it
97 if let HirType::Struct(name) = &**inner {
98 name
99 } else {
100 return None;
101 }
102 }
103 _ => return None,
104 };
105
106 // Look up the field type in the struct definition
107 let fields = self.structs.get(struct_name)?;
108 fields
109 .iter()
110 .find(|(name, _)| name == field_name)
111 .map(|(_, field_type)| field_type.clone())
112 }
113
114 fn is_pointer(&self, name: &str) -> bool {
115 matches!(self.get_type(name), Some(HirType::Pointer(_)))
116 }
117
118 fn is_option(&self, name: &str) -> bool {
119 matches!(self.get_type(name), Some(HirType::Option(_)))
120 }
121
122 /// Infer the type of an expression based on the context.
123 /// Returns None if the type cannot be inferred.
124 fn infer_expression_type(&self, expr: &HirExpression) -> Option<HirType> {
125 match expr {
126 HirExpression::Variable(name) => self.get_type(name).cloned(),
127 HirExpression::Dereference(inner) => {
128 // If inner is *mut T, then *inner is T
129 if let Some(HirType::Pointer(pointee_type)) = self.infer_expression_type(inner) {
130 Some(*pointee_type)
131 } else {
132 None
133 }
134 }
135 HirExpression::ArrayIndex { array, index: _ } => {
136 // If array is [T; N] or *mut T, then array[i] is T
137 if let Some(array_type) = self.infer_expression_type(array) {
138 match array_type {
139 HirType::Array { element_type, .. } => Some(*element_type),
140 HirType::Pointer(element_type) => Some(*element_type),
141 _ => None,
142 }
143 } else {
144 None
145 }
146 }
147 _ => None,
148 }
149 }
150}
151
152/// Code generator for converting HIR to Rust source code.
153#[derive(Debug, Clone)]
154pub struct CodeGenerator {
155 box_transformer: box_transform::BoxTransformer,
156}
157
158impl CodeGenerator {
159 /// Create a new code generator.
160 ///
161 /// # Examples
162 ///
163 /// ```
164 /// use decy_codegen::CodeGenerator;
165 ///
166 /// let codegen = CodeGenerator::new();
167 /// ```
168 pub fn new() -> Self {
169 Self {
170 box_transformer: box_transform::BoxTransformer::new(),
171 }
172 }
173
174 /// Generate Rust code for a macro definition.
175 ///
176 /// Transforms C #define macros to Rust const declarations (for object-like macros)
177 /// or inline functions (for function-like macros).
178 ///
179 /// # Supported Macro Types (DECY-098c)
180 ///
181 /// **Object-like macros** (constants) are fully supported:
182 /// - `#define MAX 100` → `const MAX: i32 = 100;`
183 /// - `#define PI 3.14159` → `const PI: f64 = 3.14159;`
184 /// - `#define GREETING "Hello"` → `const GREETING: &str = "Hello";`
185 ///
186 /// **Function-like macros** are not yet supported (DECY-098d):
187 /// - `#define SQR(x) ((x) * (x))` → Error
188 ///
189 /// # Type Inference
190 ///
191 /// Types are automatically inferred from the macro body:
192 /// - String literals → `&str`
193 /// - Character literals → `char`
194 /// - Floating point → `f64`
195 /// - Integers (including hex/octal) → `i32`
196 ///
197 /// # Edge Cases
198 ///
199 /// - Empty macros generate comments: `#define EMPTY` → `// Empty macro: EMPTY`
200 /// - Macro names are preserved exactly (SCREAMING_SNAKE_CASE maintained)
201 ///
202 /// # Errors
203 ///
204 /// Returns an error if:
205 /// - The macro is function-like (not yet implemented)
206 ///
207 /// # Examples
208 ///
209 /// ```
210 /// use decy_codegen::CodeGenerator;
211 /// use decy_hir::HirMacroDefinition;
212 ///
213 /// let generator = CodeGenerator::new();
214 /// let macro_def = HirMacroDefinition::new_object_like("MAX".to_string(), "100".to_string());
215 /// let rust_code = generator.generate_macro(¯o_def).unwrap();
216 /// assert!(rust_code.contains("const MAX"));
217 /// # Ok::<(), anyhow::Error>(())
218 /// ```
219 ///
220 /// # Reference
221 ///
222 /// - K&R §4.11: Macro Substitution
223 /// - ISO C99 §6.10.3: Macro replacement
224 pub fn generate_macro(
225 &self,
226 macro_def: &decy_hir::HirMacroDefinition,
227 ) -> anyhow::Result<String> {
228 if macro_def.is_function_like() {
229 // Generate inline function for function-like macros
230 return self.generate_function_like_macro(macro_def);
231 }
232
233 // Object-like macro (constant)
234 let name = macro_def.name();
235 let body = macro_def.body();
236
237 // Handle empty macros
238 if body.is_empty() {
239 return Ok(format!("// Empty macro: {}", name));
240 }
241
242 // Infer type from macro body
243 let (rust_type, rust_value) = self.infer_macro_type(body)?;
244
245 Ok(format!("const {}: {} = {};", name, rust_type, rust_value))
246 }
247
248 /// Generate Rust inline function from function-like macro.
249 ///
250 /// Transforms C function-like macros to Rust inline functions:
251 /// - `#define SQR(x) ((x) * (x))` → `fn sqr(x: i32) -> i32 { x * x }`
252 /// - `#define MAX(a, b) ((a) > (b) ? (a) : (b))` → `fn max(a: i32, b: i32) -> i32 { if a > b { a } else { b } }`
253 ///
254 /// # Features
255 ///
256 /// - Converts macro name from SCREAMING_SNAKE_CASE to snake_case
257 /// - Infers parameter types (defaults to i32)
258 /// - Infers return type from expression
259 /// - Adds `#[inline]` attribute for performance
260 /// - Transforms ternary operator (? :) to if-else
261 /// - Removes unnecessary parentheses
262 fn generate_function_like_macro(
263 &self,
264 macro_def: &decy_hir::HirMacroDefinition,
265 ) -> anyhow::Result<String> {
266 let name = macro_def.name();
267 let params = macro_def.parameters();
268 let body = macro_def.body();
269
270 // Convert SCREAMING_SNAKE_CASE to snake_case
271 let fn_name = self.convert_to_snake_case(name);
272
273 // Generate parameter list (default to i32 for now)
274 let param_list = params
275 .iter()
276 .map(|p| format!("{}: i32", p))
277 .collect::<Vec<_>>()
278 .join(", ");
279
280 // Transform macro body to Rust expression
281 let rust_body = self.transform_macro_body(body, params)?;
282
283 // Infer return type from body
284 let return_type = self.infer_return_type(body);
285
286 // Generate function
287 let result = format!(
288 "#[inline]\nfn {}({}) -> {} {{\n {}\n}}",
289 fn_name, param_list, return_type, rust_body
290 );
291
292 Ok(result)
293 }
294
295 /// Convert SCREAMING_SNAKE_CASE to snake_case.
296 fn convert_to_snake_case(&self, name: &str) -> String {
297 name.to_lowercase()
298 }
299
300 /// Transform C macro body to Rust expression.
301 ///
302 /// Transformations:
303 /// - Remove outer parentheses: ((x) * (x)) → x * x
304 /// - Ternary operator: (a) > (b) ? (a) : (b) → if a > b { a } else { b }
305 /// - Remove parameter parentheses: (x) → x
306 fn transform_macro_body(&self, body: &str, params: &[String]) -> anyhow::Result<String> {
307 let mut result = body.to_string();
308
309 // Check for ternary operator
310 if result.contains('?') && result.contains(':') {
311 result = self.transform_ternary(&result)?;
312 } else {
313 // Remove unnecessary parentheses around parameters
314 for param in params {
315 result = result.replace(&format!("({})", param), param);
316 }
317
318 // Remove outer parentheses if present
319 result = self.remove_outer_parens(&result);
320
321 // Add spaces around operators for readability
322 result = self.add_operator_spaces(&result);
323 }
324
325 Ok(result)
326 }
327
328 /// Transform C ternary operator to Rust if-else.
329 ///
330 /// Example: ((a)>(b)?(a):(b)) → if a > b { a } else { b }
331 fn transform_ternary(&self, expr: &str) -> anyhow::Result<String> {
332 // Find the ? and : positions
333 let question_pos = expr.find('?').unwrap_or(0);
334 let colon_pos = expr.rfind(':').unwrap_or(0);
335
336 if question_pos == 0 || colon_pos == 0 || colon_pos <= question_pos {
337 // Malformed ternary, return as-is
338 return Ok(expr.to_string());
339 }
340
341 // Extract parts
342 let condition = expr[..question_pos].trim();
343 let true_expr = expr[question_pos + 1..colon_pos].trim();
344 let false_expr = expr[colon_pos + 1..].trim();
345
346 // Clean up each part
347 let condition = self.remove_outer_parens(condition);
348 let condition = self.clean_expression(&condition);
349 let true_expr = self.remove_outer_parens(true_expr);
350 let true_expr = self.clean_expression(&true_expr);
351 let false_expr = self.remove_outer_parens(false_expr);
352 let false_expr = self.clean_expression(&false_expr);
353
354 Ok(format!(
355 "if {} {{ {} }} else {{ {} }}",
356 condition, true_expr, false_expr
357 ))
358 }
359
360 /// Remove outer parentheses from expression.
361 fn remove_outer_parens(&self, expr: &str) -> String {
362 Self::remove_outer_parens_impl(expr)
363 }
364
365 /// Implementation of remove_outer_parens (recursive helper).
366 fn remove_outer_parens_impl(expr: &str) -> String {
367 let trimmed = expr.trim();
368 if trimmed.starts_with('(') && trimmed.ends_with(')') {
369 // Check if these are matching outer parens
370 let mut depth = 0;
371 let chars: Vec<char> = trimmed.chars().collect();
372 for (i, ch) in chars.iter().enumerate() {
373 match ch {
374 '(' => depth += 1,
375 ')' => {
376 depth -= 1;
377 if depth == 0 && i < chars.len() - 1 {
378 // Found closing paren before end, not outer parens
379 return trimmed.to_string();
380 }
381 }
382 _ => {}
383 }
384 }
385 // These are outer parens, remove them
386 return Self::remove_outer_parens_impl(&trimmed[1..trimmed.len() - 1]);
387 }
388 trimmed.to_string()
389 }
390
391 /// Clean expression by removing parameter parentheses.
392 fn clean_expression(&self, expr: &str) -> String {
393 let mut result = expr.to_string();
394
395 // Handle negation: -(x) → -x (preserve the minus)
396 result = result.replace("-(x)", "-x");
397 result = result.replace("-(a)", "-a");
398 result = result.replace("-(b)", "-b");
399 result = result.replace("-(c)", "-c");
400 result = result.replace("-(n)", "-n");
401
402 // Remove parentheses around single identifiers (not negated)
403 // This is a simplified version - could be enhanced
404 result = result.replace("(x)", "x");
405 result = result.replace("(a)", "a");
406 result = result.replace("(b)", "b");
407 result = result.replace("(c)", "c");
408 result = result.replace("(n)", "n");
409
410 // Add spaces around operators
411 result = self.add_operator_spaces(&result);
412
413 result
414 }
415
416 /// Add spaces around operators for readability.
417 fn add_operator_spaces(&self, expr: &str) -> String {
418 let mut result = expr.to_string();
419
420 // Add spaces around comparison operators
421 result = result.replace(">", " > ");
422 result = result.replace("<", " < ");
423 result = result.replace("==", " == ");
424 result = result.replace("!=", " != ");
425 result = result.replace(">=", " >= ");
426 result = result.replace("<=", " <= ");
427
428 // Add spaces around logical operators (do this before arithmetic to avoid issues)
429 result = result.replace("&&", " && ");
430 result = result.replace("||", " || ");
431
432 // Add spaces around arithmetic operators
433 result = result.replace("+", " + ");
434 // Note: Don't blindly replace "-" as it could be unary minus
435 // Only replace if it's not at the start or after a space
436 let chars: Vec<char> = result.chars().collect();
437 let mut new_result = String::new();
438 for (i, ch) in chars.iter().enumerate() {
439 if *ch == '-' {
440 // Check if this is a binary minus (has non-space before it)
441 if i > 0 && !chars[i - 1].is_whitespace() && chars[i - 1] != '(' {
442 new_result.push(' ');
443 new_result.push(*ch);
444 new_result.push(' ');
445 } else {
446 // Unary minus, keep as-is
447 new_result.push(*ch);
448 }
449 } else {
450 new_result.push(*ch);
451 }
452 }
453 result = new_result;
454
455 result = result.replace("*", " * ");
456 result = result.replace("/", " / ");
457 result = result.replace("%", " % ");
458
459 // Clean up multiple spaces
460 while result.contains(" ") {
461 result = result.replace(" ", " ");
462 }
463
464 result.trim().to_string()
465 }
466
467 /// Infer return type from macro body.
468 ///
469 /// Simple heuristic:
470 /// - Contains ternary operator (? :) → return type of branches (check for comparison at top level)
471 /// - Contains comparison operators at top level (not in ternary) → bool
472 /// - Contains logical operators (&&, ||) → bool
473 /// - Default → i32
474 fn infer_return_type(&self, body: &str) -> String {
475 // Check for ternary - return type depends on the branches, not the condition
476 if body.contains('?') && body.contains(':') {
477 // For ternary, the return type is determined by what's returned, not the condition
478 // In most C macros like MAX(a,b), the return type is i32 even though condition is bool
479 return "i32".to_string();
480 }
481
482 // Check for logical operators (&&, ||) at top level
483 if body.contains("&&") || body.contains("||") {
484 return "bool".to_string();
485 }
486
487 // Check if it's a standalone comparison (no ternary)
488 if (body.contains('>') || body.contains('<') || body.contains("==") || body.contains("!="))
489 && !body.contains('?')
490 {
491 // Standalone comparison returns bool
492 "bool".to_string()
493 } else {
494 // Default to i32
495 "i32".to_string()
496 }
497 }
498
499 /// Infer the Rust type and value from a C macro body.
500 ///
501 /// This function analyzes the macro body string and determines the appropriate
502 /// Rust type and formatted value.
503 ///
504 /// # Type Inference Rules
505 ///
506 /// - String literals (`"text"`) → `&str`
507 /// - Character literals (`'c'`) → `char`
508 /// - Floating point (contains `.` or `e`/`E`) → `f64`
509 /// - Hexadecimal (`0xFF`) → `i32` (preserves hex format)
510 /// - Octal (`0755`) → `i32` (preserves octal format)
511 /// - Integers (parseable as i32) → `i32`
512 /// - Default (expressions) → `i32`
513 ///
514 /// # Returns
515 ///
516 /// Returns a tuple of (rust_type, rust_value) where:
517 /// - `rust_type`: The Rust type as a string (e.g., "i32", "&str")
518 /// - `rust_value`: The formatted value (e.g., "100", "\"Hello\"")
519 ///
520 /// # Examples
521 ///
522 /// ```
523 /// # use decy_codegen::CodeGenerator;
524 /// let generator = CodeGenerator::new();
525 /// // This is a private method, but tested through generate_macro
526 /// # Ok::<(), anyhow::Error>(())
527 /// ```
528 fn infer_macro_type(&self, body: &str) -> anyhow::Result<(String, String)> {
529 let body = body.trim();
530
531 // String literal: "..." → &str
532 if body.starts_with('"') && body.ends_with('"') {
533 return Ok(("&str".to_string(), body.to_string()));
534 }
535
536 // Character literal: '...' → char
537 if body.starts_with('\'') && body.ends_with('\'') {
538 return Ok(("char".to_string(), body.to_string()));
539 }
540
541 // Floating point: contains '.' or 'e'/'E' → f64
542 if body.contains('.') || body.contains('e') || body.contains('E') {
543 return Ok(("f64".to_string(), body.to_string()));
544 }
545
546 // Hexadecimal: 0x... or 0X... → i32 (keep hex format)
547 if body.starts_with("0x") || body.starts_with("0X") {
548 return Ok(("i32".to_string(), body.to_string()));
549 }
550
551 // Octal: 0... → i32
552 if body.starts_with('0') && body.len() > 1 && body.chars().nth(1).unwrap().is_ascii_digit()
553 {
554 return Ok(("i32".to_string(), body.to_string()));
555 }
556
557 // Try to parse as integer (handles negative numbers too)
558 if body.parse::<i32>().is_ok() {
559 return Ok(("i32".to_string(), body.to_string()));
560 }
561
562 // Default: treat as i32 expression
563 Ok(("i32".to_string(), body.to_string()))
564 }
565
566 /// Get the Box transformer.
567 pub fn box_transformer(&self) -> &box_transform::BoxTransformer {
568 &self.box_transformer
569 }
570
571 /// Map HIR type to Rust type string.
572 ///
573 /// # Examples
574 ///
575 /// ```
576 /// use decy_codegen::CodeGenerator;
577 /// use decy_hir::HirType;
578 ///
579 /// assert_eq!(CodeGenerator::map_type(&HirType::Int), "i32");
580 /// assert_eq!(CodeGenerator::map_type(&HirType::Float), "f32");
581 /// assert_eq!(CodeGenerator::map_type(&HirType::Box(Box::new(HirType::Int))), "Box<i32>");
582 /// ```
583 pub fn map_type(hir_type: &HirType) -> String {
584 match hir_type {
585 HirType::Void => "()".to_string(),
586 HirType::Int => "i32".to_string(),
587 HirType::Float => "f32".to_string(),
588 HirType::Double => "f64".to_string(),
589 HirType::Char => "u8".to_string(),
590 HirType::Pointer(inner) => {
591 format!("*mut {}", Self::map_type(inner))
592 }
593 HirType::Box(inner) => {
594 format!("Box<{}>", Self::map_type(inner))
595 }
596 HirType::Vec(inner) => {
597 format!("Vec<{}>", Self::map_type(inner))
598 }
599 HirType::Option(inner) => {
600 format!("Option<{}>", Self::map_type(inner))
601 }
602 HirType::Reference { inner, mutable } => {
603 // DECY-072: Special case for slices: &Vec<T> → &[T]
604 if let HirType::Vec(element_type) = &**inner {
605 let element_str = Self::map_type(element_type);
606 if *mutable {
607 format!("&mut [{}]", element_str)
608 } else {
609 format!("&[{}]", element_str)
610 }
611 } else if *mutable {
612 format!("&mut {}", Self::map_type(inner))
613 } else {
614 format!("&{}", Self::map_type(inner))
615 }
616 }
617 HirType::Struct(name) => name.clone(),
618 HirType::Enum(name) => name.clone(),
619 HirType::Array { element_type, size } => {
620 if let Some(n) = size {
621 format!("[{}; {}]", Self::map_type(element_type), n)
622 } else {
623 // Unsized array - use slice reference
624 format!("[{}]", Self::map_type(element_type))
625 }
626 }
627 HirType::FunctionPointer {
628 param_types,
629 return_type,
630 } => {
631 // C: int (*func_ptr)(int, int); → Rust: fn(i32, i32) -> i32
632 let params: Vec<String> = param_types.iter().map(Self::map_type).collect();
633 let params_str = params.join(", ");
634
635 // Skip return type annotation for void
636 if matches!(**return_type, HirType::Void) {
637 format!("fn({})", params_str)
638 } else {
639 format!("fn({}) -> {}", params_str, Self::map_type(return_type))
640 }
641 }
642 HirType::StringLiteral => "&str".to_string(),
643 HirType::OwnedString => "String".to_string(),
644 HirType::StringReference => "&str".to_string(),
645 HirType::Union(_) => {
646 // Unions will be transformed to Rust enums
647 // For now, return a placeholder
648 "/* Union type */".to_string()
649 }
650 }
651 }
652
653 /// Map C type name from sizeof to Rust type string.
654 ///
655 /// Handles type names as strings from sizeof expressions.
656 /// Examples: "int" → "i32", "struct Data" → "Data"
657 fn map_sizeof_type(&self, c_type_name: &str) -> String {
658 let trimmed = c_type_name.trim();
659
660 // Handle basic C types
661 match trimmed {
662 "int" => "i32".to_string(),
663 "float" => "f32".to_string(),
664 "double" => "f64".to_string(),
665 "char" => "u8".to_string(),
666 "void" => "()".to_string(),
667 _ => {
668 // Handle "struct TypeName" → "TypeName"
669 if let Some(struct_name) = trimmed.strip_prefix("struct ") {
670 struct_name.trim().to_string()
671 } else {
672 // Keep custom type names as-is
673 trimmed.to_string()
674 }
675 }
676 }
677 }
678
679 /// Generate code for an expression.
680 #[allow(clippy::only_used_in_recursion)]
681 pub fn generate_expression(&self, expr: &HirExpression) -> String {
682 self.generate_expression_with_context(expr, &TypeContext::new())
683 }
684
685 /// Generate code for an expression with type context for pointer arithmetic.
686 #[allow(clippy::only_used_in_recursion)]
687 fn generate_expression_with_context(&self, expr: &HirExpression, ctx: &TypeContext) -> String {
688 self.generate_expression_with_target_type(expr, ctx, None)
689 }
690
691 /// Generate code for an expression with optional target type hint for null pointer detection.
692 /// If target_type is Some(HirType::Pointer(_)) and expr is IntLiteral(0), generates std::ptr::null_mut().
693 #[allow(clippy::only_used_in_recursion)]
694 fn generate_expression_with_target_type(
695 &self,
696 expr: &HirExpression,
697 ctx: &TypeContext,
698 target_type: Option<&HirType>,
699 ) -> String {
700 match expr {
701 HirExpression::IntLiteral(val) => {
702 // Check if assigning 0 to a pointer type
703 if *val == 0 {
704 if let Some(HirType::Pointer(_)) = target_type {
705 return "std::ptr::null_mut()".to_string();
706 }
707 }
708 val.to_string()
709 }
710 HirExpression::StringLiteral(s) => format!("\"{}\"", s),
711 HirExpression::Variable(name) => name.clone(),
712 HirExpression::BinaryOp { op, left, right } => {
713 // Check for Option comparison with NULL → is_none() / is_some()
714 // p == NULL → p.is_none(), p != NULL → p.is_some()
715 if matches!(op, BinaryOperator::Equal | BinaryOperator::NotEqual) {
716 // Check if left is an Option and right is NULL
717 if let HirExpression::Variable(var_name) = &**left {
718 if ctx.is_option(var_name) && matches!(**right, HirExpression::NullLiteral)
719 {
720 return match op {
721 BinaryOperator::Equal => format!("{}.is_none()", var_name),
722 BinaryOperator::NotEqual => format!("{}.is_some()", var_name),
723 _ => unreachable!(),
724 };
725 }
726 }
727 // Check if right is an Option and left is NULL (NULL == p or NULL != p)
728 if let HirExpression::Variable(var_name) = &**right {
729 if ctx.is_option(var_name) && matches!(**left, HirExpression::NullLiteral) {
730 return match op {
731 BinaryOperator::Equal => format!("{}.is_none()", var_name),
732 BinaryOperator::NotEqual => format!("{}.is_some()", var_name),
733 _ => unreachable!(),
734 };
735 }
736 }
737
738 // Check for pointer comparison with 0 (null pointer comparison)
739 // ptr == 0 or ptr != 0 should become ptr == std::ptr::null_mut() or ptr != std::ptr::null_mut()
740 // Check if left is a pointer and right is 0
741 if let HirExpression::Variable(var_name) = &**left {
742 if ctx.is_pointer(var_name) {
743 if let HirExpression::IntLiteral(0) = **right {
744 let op_str = Self::binary_operator_to_string(op);
745 return format!("{} {} std::ptr::null_mut()", var_name, op_str);
746 }
747 }
748 }
749 // Check if right is a pointer and left is 0 (0 == ptr or 0 != ptr)
750 if let HirExpression::Variable(var_name) = &**right {
751 if ctx.is_pointer(var_name) {
752 if let HirExpression::IntLiteral(0) = **left {
753 let op_str = Self::binary_operator_to_string(op);
754 return format!("std::ptr::null_mut() {} {}", op_str, var_name);
755 }
756 }
757 }
758 }
759
760 let left_code = self.generate_expression_with_context(left, ctx);
761 let right_code = self.generate_expression_with_context(right, ctx);
762 let op_str = Self::binary_operator_to_string(op);
763
764 // Add parentheses for nested binary operations
765 let left_str = if matches!(**left, HirExpression::BinaryOp { .. }) {
766 format!("({})", left_code)
767 } else {
768 left_code.clone()
769 };
770
771 let right_str = if matches!(**right, HirExpression::BinaryOp { .. }) {
772 format!("({})", right_code)
773 } else {
774 right_code.clone()
775 };
776
777 // DECY-041: Detect pointer arithmetic using type context
778 if matches!(op, BinaryOperator::Add | BinaryOperator::Subtract) {
779 if let HirExpression::Variable(var_name) = &**left {
780 if ctx.is_pointer(var_name) {
781 // This is pointer arithmetic - generate unsafe pointer method calls
782 return match op {
783 BinaryOperator::Add => {
784 format!(
785 "unsafe {{ {}.wrapping_add({} as usize) }}",
786 left_str, right_str
787 )
788 }
789 BinaryOperator::Subtract => {
790 // Check if right is also a pointer (ptr - ptr) or integer (ptr - offset)
791 if let HirExpression::Variable(right_var) = &**right {
792 if ctx.is_pointer(right_var) {
793 // ptr - ptr: calculate difference (returns isize, cast to i32 for C compatibility)
794 format!(
795 "unsafe {{ {}.offset_from({}) as i32 }}",
796 left_str, right_str
797 )
798 } else {
799 // ptr - integer offset
800 format!(
801 "unsafe {{ {}.wrapping_sub({} as usize) }}",
802 left_str, right_str
803 )
804 }
805 } else {
806 // ptr - integer offset (literal or expression)
807 format!(
808 "unsafe {{ {}.wrapping_sub({} as usize) }}",
809 left_str, right_str
810 )
811 }
812 }
813 _ => unreachable!(),
814 };
815 }
816 }
817 }
818
819 format!("{} {} {}", left_str, op_str, right_str)
820 }
821 HirExpression::Dereference(inner) => {
822 let inner_code = self.generate_expression_with_context(inner, ctx);
823
824 // DECY-041: Check if dereferencing a raw pointer - if so, wrap in unsafe
825 if let HirExpression::Variable(var_name) = &**inner {
826 if ctx.is_pointer(var_name) {
827 return format!("unsafe {{ *{} }}", inner_code);
828 }
829 }
830
831 format!("*{}", inner_code)
832 }
833 HirExpression::AddressOf(inner) => {
834 let inner_code = self.generate_expression_with_context(inner, ctx);
835 // Add parentheses for non-trivial expressions
836 if matches!(**inner, HirExpression::Dereference(_)) {
837 format!("&({})", inner_code)
838 } else {
839 format!("&{}", inner_code)
840 }
841 }
842 HirExpression::UnaryOp { op, operand } => {
843 use decy_hir::UnaryOperator;
844 match op {
845 // Post-increment: x++ → { let tmp = x; x += 1; tmp }
846 // Returns old value before incrementing
847 UnaryOperator::PostIncrement => {
848 let operand_code = self.generate_expression_with_context(operand, ctx);
849 format!(
850 "{{ let tmp = {}; {} += 1; tmp }}",
851 operand_code, operand_code
852 )
853 }
854 // Post-decrement: x-- → { let tmp = x; x -= 1; tmp }
855 // Returns old value before decrementing
856 UnaryOperator::PostDecrement => {
857 let operand_code = self.generate_expression_with_context(operand, ctx);
858 format!(
859 "{{ let tmp = {}; {} -= 1; tmp }}",
860 operand_code, operand_code
861 )
862 }
863 // Pre-increment: ++x → { x += 1; x }
864 // Increments first, then returns new value
865 UnaryOperator::PreIncrement => {
866 let operand_code = self.generate_expression_with_context(operand, ctx);
867 format!("{{ {} += 1; {} }}", operand_code, operand_code)
868 }
869 // Pre-decrement: --x → { x -= 1; x }
870 // Decrements first, then returns new value
871 UnaryOperator::PreDecrement => {
872 let operand_code = self.generate_expression_with_context(operand, ctx);
873 format!("{{ {} -= 1; {} }}", operand_code, operand_code)
874 }
875 // Simple prefix operators
876 _ => {
877 let op_str = Self::unary_operator_to_string(op);
878 let operand_code = self.generate_expression_with_context(operand, ctx);
879 format!("{}{}", op_str, operand_code)
880 }
881 }
882 }
883 HirExpression::FunctionCall {
884 function,
885 arguments,
886 } => {
887 // Special handling for standard library functions
888 match function.as_str() {
889 // strlen(s) → s.len()
890 // Reference: K&R §B3, ISO C99 §7.21.6.3
891 "strlen" => {
892 if arguments.len() == 1 {
893 format!(
894 "{}.len()",
895 self.generate_expression_with_context(&arguments[0], ctx)
896 )
897 } else {
898 // Invalid strlen call - shouldn't happen, but handle gracefully
899 let args: Vec<String> = arguments
900 .iter()
901 .map(|arg| self.generate_expression_with_context(arg, ctx))
902 .collect();
903 format!("{}({})", function, args.join(", "))
904 }
905 }
906 // strcpy(dest, src) → src.to_string()
907 // Reference: K&R §B3, ISO C99 §7.21.3.1
908 // strcpy copies src to dest and returns dest pointer.
909 // In Rust, we transform to String operation: src.to_string()
910 // This prevents buffer overflow (the primary safety benefit)
911 "strcpy" => {
912 if arguments.len() == 2 {
913 // strcpy(dest, src) → src.to_string()
914 // We generate the source string operation
915 // The destination assignment is handled by the statement context
916 format!(
917 "{}.to_string()",
918 self.generate_expression_with_context(&arguments[1], ctx)
919 )
920 } else {
921 // Invalid strcpy call - shouldn't happen, but handle gracefully
922 let args: Vec<String> = arguments
923 .iter()
924 .map(|arg| self.generate_expression_with_context(arg, ctx))
925 .collect();
926 format!("{}({})", function, args.join(", "))
927 }
928 }
929 // Default: pass through function call as-is
930 _ => {
931 let args: Vec<String> = arguments
932 .iter()
933 .map(|arg| self.generate_expression_with_context(arg, ctx))
934 .collect();
935 format!("{}({})", function, args.join(", "))
936 }
937 }
938 }
939 HirExpression::FieldAccess { object, field } => {
940 format!(
941 "{}.{}",
942 self.generate_expression_with_context(object, ctx),
943 field
944 )
945 }
946 HirExpression::PointerFieldAccess { pointer, field } => {
947 // In Rust, ptr->field becomes (*ptr).field
948 // However, if the pointer is already a field access (ptr->field1->field2),
949 // we should generate (*ptr).field1.field2 not (*(*ptr).field1).field2
950 match &**pointer {
951 // If the pointer is itself a field access expression, we can chain with .
952 HirExpression::PointerFieldAccess { .. }
953 | HirExpression::FieldAccess { .. } => {
954 format!(
955 "{}.{}",
956 self.generate_expression_with_context(pointer, ctx),
957 field
958 )
959 }
960 // For other expressions (variables, array index, etc), we need explicit deref
961 _ => {
962 format!(
963 "(*{}).{}",
964 self.generate_expression_with_context(pointer, ctx),
965 field
966 )
967 }
968 }
969 }
970 HirExpression::ArrayIndex { array, index } => {
971 let array_code = self.generate_expression_with_context(array, ctx);
972 let index_code = self.generate_expression_with_context(index, ctx);
973
974 // DECY-041: Check if array is a raw pointer - if so, use unsafe pointer arithmetic
975 if let HirExpression::Variable(var_name) = &**array {
976 if ctx.is_pointer(var_name) {
977 // Raw pointer indexing: arr[i] becomes unsafe { *arr.add(i as usize) }
978 return format!(
979 "unsafe {{ *{}.add({} as usize) }}",
980 array_code, index_code
981 );
982 }
983 }
984
985 // Regular array/slice indexing
986 // DECY-072: Cast index to usize for slice indexing
987 format!("{}[{} as usize]", array_code, index_code)
988 }
989 HirExpression::SliceIndex { slice, index, .. } => {
990 // DECY-070 GREEN: Generate safe slice indexing (0 unsafe blocks!)
991 // SliceIndex represents pointer arithmetic transformed to safe indexing
992 let slice_code = self.generate_expression_with_context(slice, ctx);
993 let index_code = self.generate_expression_with_context(index, ctx);
994 // Generate: slice[index] - guaranteed safe, bounds-checked at runtime
995 format!("{}[{}]", slice_code, index_code)
996 }
997 HirExpression::Sizeof { type_name } => {
998 // sizeof(int) → std::mem::size_of::<i32>() as i32
999 // sizeof(struct Data) → std::mem::size_of::<Data>() as i32
1000 // Note: size_of returns usize, but C's sizeof returns int (typically i32)
1001 let rust_type = self.map_sizeof_type(type_name);
1002 format!("std::mem::size_of::<{}>() as i32", rust_type)
1003 }
1004 HirExpression::NullLiteral => {
1005 // NULL → None
1006 "None".to_string()
1007 }
1008 HirExpression::IsNotNull(inner) => {
1009 // p != NULL → if let Some(p) = p
1010 // This is a helper expression for generating Option checks
1011 // In actual codegen, we transform if (p) to if let Some(_) = p
1012 let inner_code = self.generate_expression_with_context(inner, ctx);
1013 format!("if let Some(_) = {}", inner_code)
1014 }
1015 HirExpression::Calloc {
1016 count,
1017 element_type,
1018 } => {
1019 // calloc(n, sizeof(T)) → vec![0T; n]
1020 // Generate zero-initialized vec![default; count]
1021 let count_code = self.generate_expression_with_context(count, ctx);
1022
1023 // Get default value with type suffix for clarity
1024 let default_value = match element_type.as_ref() {
1025 HirType::Int => "0i32",
1026 HirType::Float => "0.0f32",
1027 HirType::Double => "0.0f64",
1028 HirType::Char => "0u8",
1029 _ => &Self::default_value_for_type(element_type),
1030 };
1031
1032 format!("vec![{}; {}]", default_value, count_code)
1033 }
1034 HirExpression::Malloc { size } => {
1035 // malloc(size) should have been transformed to Box or Vec by analyzer
1036 // If we're generating this directly, treat it as Box::new(default)
1037 // Note: The proper transformation should happen at HIR level via PatternDetector
1038
1039 // Try to detect if this is an array allocation (n * sizeof(T))
1040 // If so, generate Vec::with_capacity
1041 if let HirExpression::BinaryOp {
1042 op: decy_hir::BinaryOperator::Multiply,
1043 left,
1044 ..
1045 } = size.as_ref()
1046 {
1047 // This looks like n * sizeof(T) → Vec::with_capacity(n)
1048 let capacity_code = self.generate_expression_with_context(left, ctx);
1049 format!("Vec::with_capacity({})", capacity_code)
1050 } else {
1051 // Single allocation → Box::new(default)
1052 "Box::new(0i32)".to_string()
1053 }
1054 }
1055 HirExpression::Realloc { pointer, new_size } => {
1056 // realloc(ptr, new_size) transformation depends on context:
1057 // 1. realloc(NULL, size) → treat as malloc (Vec allocation)
1058 // 2. realloc(ptr, 0) → treat as free (RAII comment or clear)
1059 // 3. realloc(ptr, new_size) → vec.resize(new_count, default)
1060 //
1061 // Since we're generating an expression here, we'll return a placeholder
1062 // The actual transformation should happen in Assignment statement handling
1063 // For now, just generate a comment indicating this needs special handling
1064
1065 // Check if pointer is NULL → malloc equivalent
1066 if matches!(**pointer, HirExpression::NullLiteral) {
1067 // realloc(NULL, size) → vec![default; count]
1068 if let HirExpression::BinaryOp {
1069 op: decy_hir::BinaryOperator::Multiply,
1070 left,
1071 ..
1072 } = new_size.as_ref()
1073 {
1074 let count_code = self.generate_expression_with_context(left, ctx);
1075 format!("vec![0i32; {}]", count_code)
1076 } else {
1077 "Vec::new()".to_string()
1078 }
1079 } else {
1080 // realloc(ptr, size) - this should be handled at statement level
1081 // For expression context, return the pointer unchanged as a placeholder
1082 self.generate_expression_with_context(pointer, ctx)
1083 }
1084 }
1085 HirExpression::StringMethodCall {
1086 receiver,
1087 method,
1088 arguments,
1089 } => {
1090 let receiver_code = self.generate_expression_with_context(receiver, ctx);
1091 if arguments.is_empty() {
1092 // DECY-072: Cast .len() to i32 for slices/arrays (used with i32 loop counters)
1093 // String .len() returns usize which is typically what's needed for strings
1094 // But array/slice .len() often needs i32 for C loop patterns
1095 // We cast for all cases to maintain C semantics (where sizeof returns int)
1096 if method == "len" {
1097 format!("{}.{}() as i32", receiver_code, method)
1098 } else {
1099 format!("{}.{}()", receiver_code, method)
1100 }
1101 } else {
1102 let args: Vec<String> = arguments
1103 .iter()
1104 .map(|arg| {
1105 // For clone_into, we need &mut on the destination
1106 if method == "clone_into" {
1107 format!("&mut {}", self.generate_expression_with_context(arg, ctx))
1108 } else {
1109 self.generate_expression_with_context(arg, ctx)
1110 }
1111 })
1112 .collect();
1113 format!("{}.{}({})", receiver_code, method, args.join(", "))
1114 }
1115 }
1116 HirExpression::Cast { target_type, expr } => {
1117 // C: (int)x → Rust: x as i32
1118 // Sprint 19 Feature (DECY-059)
1119 let expr_code = self.generate_expression_with_context(expr, ctx);
1120 let rust_type = Self::map_type(target_type);
1121
1122 // Wrap in parentheses if the expression is a binary operation
1123 let expr_str = if matches!(**expr, HirExpression::BinaryOp { .. }) {
1124 format!("({})", expr_code)
1125 } else {
1126 expr_code
1127 };
1128
1129 format!("{} as {}", expr_str, rust_type)
1130 }
1131 HirExpression::CompoundLiteral {
1132 literal_type,
1133 initializers,
1134 } => {
1135 // C: (struct Point){10, 20} → Rust: Point { field0: 10, field1: 20 }
1136 // C: (int[]){1, 2, 3} → Rust: vec![1, 2, 3] or [1, 2, 3]
1137 // Sprint 19 Feature (DECY-060)
1138 match literal_type {
1139 HirType::Struct(name) => {
1140 // Generate struct literal: StructName { field0: val0, field1: val1, ... }
1141 if initializers.is_empty() {
1142 // Empty struct: Point {}
1143 format!("{} {{}}", name)
1144 } else {
1145 let fields: Vec<String> = initializers
1146 .iter()
1147 .enumerate()
1148 .map(|(i, init)| {
1149 let init_code =
1150 self.generate_expression_with_context(init, ctx);
1151 format!("field{}: {}", i, init_code)
1152 })
1153 .collect();
1154 format!("{} {{ {} }}", name, fields.join(", "))
1155 }
1156 }
1157 HirType::Array { .. } => {
1158 // Generate array literal: vec![1, 2, 3] or [1, 2, 3]
1159 if initializers.is_empty() {
1160 "vec![]".to_string()
1161 } else {
1162 let elements: Vec<String> = initializers
1163 .iter()
1164 .map(|init| self.generate_expression_with_context(init, ctx))
1165 .collect();
1166 format!("vec![{}]", elements.join(", "))
1167 }
1168 }
1169 _ => {
1170 // For other types, generate a reasonable default
1171 // This is a simplified implementation
1172 format!(
1173 "/* Compound literal of type {} */",
1174 Self::map_type(literal_type)
1175 )
1176 }
1177 }
1178 }
1179 }
1180 }
1181
1182 /// Convert unary operator to string.
1183 fn unary_operator_to_string(op: &decy_hir::UnaryOperator) -> &'static str {
1184 use decy_hir::UnaryOperator;
1185 match op {
1186 UnaryOperator::Minus => "-",
1187 UnaryOperator::LogicalNot => "!",
1188 UnaryOperator::BitwiseNot => "~",
1189 UnaryOperator::AddressOf => "&",
1190 // Post/Pre-increment/decrement are handled as block expressions
1191 // in generate_expression_with_context, so should never reach here
1192 UnaryOperator::PostIncrement
1193 | UnaryOperator::PostDecrement
1194 | UnaryOperator::PreIncrement
1195 | UnaryOperator::PreDecrement => {
1196 unreachable!("Increment/decrement operators should be handled as block expressions")
1197 }
1198 }
1199 }
1200
1201 /// Convert binary operator to string.
1202 fn binary_operator_to_string(op: &BinaryOperator) -> &'static str {
1203 match op {
1204 BinaryOperator::Add => "+",
1205 BinaryOperator::Subtract => "-",
1206 BinaryOperator::Multiply => "*",
1207 BinaryOperator::Divide => "/",
1208 BinaryOperator::Modulo => "%",
1209 BinaryOperator::Equal => "==",
1210 BinaryOperator::NotEqual => "!=",
1211 BinaryOperator::LessThan => "<",
1212 BinaryOperator::GreaterThan => ">",
1213 BinaryOperator::LessEqual => "<=",
1214 BinaryOperator::GreaterEqual => ">=",
1215 BinaryOperator::LogicalAnd => "&&",
1216 BinaryOperator::LogicalOr => "||",
1217 }
1218 }
1219
1220 /// Get default value for a type (for uninitialized variables).
1221 fn default_value_for_type(hir_type: &HirType) -> String {
1222 match hir_type {
1223 HirType::Void => "()".to_string(),
1224 HirType::Int => "0i32".to_string(),
1225 HirType::Float => "0.0f32".to_string(),
1226 HirType::Double => "0.0f64".to_string(),
1227 HirType::Char => "0u8".to_string(),
1228 HirType::Pointer(_) => "std::ptr::null_mut()".to_string(),
1229 HirType::Box(inner) => {
1230 // Box types should not use default values, they should be initialized with Box::new
1231 // This is just a fallback
1232 format!("Box::new({})", Self::default_value_for_type(inner))
1233 }
1234 HirType::Vec(_) => {
1235 // Vec types default to empty Vec
1236 "Vec::new()".to_string()
1237 }
1238 HirType::Option(_) => {
1239 // Option types default to None
1240 "None".to_string()
1241 }
1242 HirType::Reference { .. } => {
1243 // References cannot have default values - they must always be initialized
1244 // This should never be reached in valid code
1245 panic!("References must be initialized and cannot have default values")
1246 }
1247 HirType::Struct(name) => {
1248 format!("{}::default()", name)
1249 }
1250 HirType::Enum(name) => {
1251 format!("{}::default()", name)
1252 }
1253 HirType::Array { element_type, size } => {
1254 if let Some(n) = size {
1255 format!("[{}; {}]", Self::default_value_for_type(element_type), n)
1256 } else {
1257 // Unsized arrays cannot have default values - this should be initialized from parameter
1258 panic!("Unsized arrays must be initialized and cannot have default values")
1259 }
1260 }
1261 HirType::FunctionPointer { .. } => {
1262 // Function pointers cannot have meaningful default values
1263 // They must be initialized with an actual function
1264 panic!("Function pointers must be initialized and cannot have default values")
1265 }
1266 HirType::StringLiteral => {
1267 // String literals default to empty string slice
1268 r#""""#.to_string()
1269 }
1270 HirType::OwnedString => {
1271 // Owned strings default to String::new()
1272 "String::new()".to_string()
1273 }
1274 HirType::StringReference => {
1275 // String references default to empty string slice
1276 r#""""#.to_string()
1277 }
1278 HirType::Union(_) => {
1279 // Unions will be transformed to enums
1280 // Default to the first variant's default value
1281 panic!("Union types must be initialized and cannot have default values")
1282 }
1283 }
1284 }
1285
1286 /// Generate code for a statement.
1287 pub fn generate_statement(&self, stmt: &HirStatement) -> String {
1288 self.generate_statement_for_function(stmt, None)
1289 }
1290
1291 /// Generate code for a statement, with optional function context.
1292 ///
1293 /// When function_name is "main", special handling applies (DECY-AUDIT-001):
1294 /// - return N; becomes std::process::exit(N);
1295 fn generate_statement_for_function(
1296 &self,
1297 stmt: &HirStatement,
1298 function_name: Option<&str>,
1299 ) -> String {
1300 self.generate_statement_with_context(stmt, function_name, &mut TypeContext::new(), None)
1301 }
1302
1303 /// Generate code for a statement with type context for pointer arithmetic and return type for null pointer detection.
1304 fn generate_statement_with_context(
1305 &self,
1306 stmt: &HirStatement,
1307 function_name: Option<&str>,
1308 ctx: &mut TypeContext,
1309 return_type: Option<&HirType>,
1310 ) -> String {
1311 match stmt {
1312 HirStatement::VariableDeclaration {
1313 name,
1314 var_type,
1315 initializer,
1316 } => {
1317 // Check for VLA pattern: Array with size: None and an initializer
1318 // C99 VLA: int arr[n]; where n is runtime-determined
1319 // Rust: let arr = vec![0i32; n];
1320 if let HirType::Array {
1321 element_type,
1322 size: None,
1323 } = var_type
1324 {
1325 // This is a VLA - transform to Vec
1326 if let Some(size_expr) = initializer {
1327 // VLA → Vec
1328 let size_code = self.generate_expression_with_context(size_expr, ctx);
1329 let default_value = match element_type.as_ref() {
1330 HirType::Int => "0i32",
1331 HirType::Float => "0.0f32",
1332 HirType::Double => "0.0f64",
1333 HirType::Char => "0u8",
1334 _ => &Self::default_value_for_type(element_type),
1335 };
1336
1337 // Register the variable as Vec type in context
1338 ctx.add_variable(
1339 name.clone(),
1340 HirType::Vec(Box::new(element_type.as_ref().clone())),
1341 );
1342
1343 return format!(
1344 "let mut {} = vec![{}; {}];",
1345 name, default_value, size_code
1346 );
1347 }
1348 }
1349
1350 // Add variable to type context for pointer arithmetic detection
1351 ctx.add_variable(name.clone(), var_type.clone());
1352
1353 let mut code = format!("let mut {}: {}", name, Self::map_type(var_type));
1354 if let Some(init_expr) = initializer {
1355 // Special handling for Malloc expressions - use var_type to generate correct Box::new
1356 if matches!(init_expr, HirExpression::Malloc { .. }) {
1357 // Malloc → Box::new or Vec (depending on var_type)
1358 match var_type {
1359 HirType::Box(inner) => {
1360 code.push_str(&format!(
1361 " = Box::new({});",
1362 Self::default_value_for_type(inner)
1363 ));
1364 }
1365 HirType::Vec(_) => {
1366 // Extract capacity from malloc size expression
1367 if let HirExpression::Malloc { size } = init_expr {
1368 if let HirExpression::BinaryOp {
1369 op: decy_hir::BinaryOperator::Multiply,
1370 left,
1371 ..
1372 } = size.as_ref()
1373 {
1374 let capacity_code =
1375 self.generate_expression_with_context(left, ctx);
1376 code.push_str(&format!(
1377 " = Vec::with_capacity({});",
1378 capacity_code
1379 ));
1380 } else {
1381 code.push_str(" = Vec::new();");
1382 }
1383 } else {
1384 code.push_str(" = Vec::new();");
1385 }
1386 }
1387 _ => {
1388 // Default to Box::new(0i32) for other types
1389 code.push_str(" = Box::new(0i32);");
1390 }
1391 }
1392 } else {
1393 // Pass var_type as target type hint for null pointer detection
1394 code.push_str(&format!(
1395 " = {};",
1396 self.generate_expression_with_target_type(
1397 init_expr,
1398 ctx,
1399 Some(var_type)
1400 )
1401 ));
1402 }
1403 } else {
1404 // Provide default value for uninitialized variables
1405 code.push_str(&format!(" = {};", Self::default_value_for_type(var_type)));
1406 }
1407 code
1408 }
1409 HirStatement::Return(expr_opt) => {
1410 // Special handling for main function (DECY-AUDIT-001)
1411 // return N; in main becomes std::process::exit(N);
1412 if function_name == Some("main") {
1413 if let Some(expr) = expr_opt {
1414 format!(
1415 "std::process::exit({});",
1416 self.generate_expression_with_context(expr, ctx)
1417 )
1418 } else {
1419 "std::process::exit(0);".to_string()
1420 }
1421 } else if let Some(expr) = expr_opt {
1422 // Pass return type as target type hint for null pointer detection
1423 format!(
1424 "return {};",
1425 self.generate_expression_with_target_type(expr, ctx, return_type)
1426 )
1427 } else {
1428 "return;".to_string()
1429 }
1430 }
1431 HirStatement::If {
1432 condition,
1433 then_block,
1434 else_block,
1435 } => {
1436 let mut code = String::new();
1437
1438 // Generate if condition
1439 code.push_str(&format!(
1440 "if {} {{\n",
1441 self.generate_expression_with_context(condition, ctx)
1442 ));
1443
1444 // Generate then block
1445 for stmt in then_block {
1446 code.push_str(" ");
1447 code.push_str(&self.generate_statement_with_context(
1448 stmt,
1449 function_name,
1450 ctx,
1451 return_type,
1452 ));
1453 code.push('\n');
1454 }
1455
1456 // Generate else block if present
1457 if let Some(else_stmts) = else_block {
1458 code.push_str("} else {\n");
1459 for stmt in else_stmts {
1460 code.push_str(" ");
1461 code.push_str(&self.generate_statement_with_context(
1462 stmt,
1463 function_name,
1464 ctx,
1465 return_type,
1466 ));
1467 code.push('\n');
1468 }
1469 }
1470
1471 code.push('}');
1472 code
1473 }
1474 HirStatement::While { condition, body } => {
1475 let mut code = String::new();
1476
1477 // Generate while condition
1478 code.push_str(&format!(
1479 "while {} {{\n",
1480 self.generate_expression_with_context(condition, ctx)
1481 ));
1482
1483 // Generate loop body
1484 for stmt in body {
1485 code.push_str(" ");
1486 code.push_str(&self.generate_statement_with_context(
1487 stmt,
1488 function_name,
1489 ctx,
1490 return_type,
1491 ));
1492 code.push('\n');
1493 }
1494
1495 code.push('}');
1496 code
1497 }
1498 HirStatement::Break => "break;".to_string(),
1499 HirStatement::Continue => "continue;".to_string(),
1500 HirStatement::Assignment { target, value } => {
1501 // Special handling for realloc() → Vec::resize/truncate/clear
1502 if let HirExpression::Realloc { pointer, new_size } = value {
1503 // target is a String (variable name) in Assignment statements
1504 let target_var = target.clone();
1505
1506 // Check if target is a Vec type to get element type
1507 let element_type = if let Some(HirType::Vec(inner)) = ctx.get_type(&target_var)
1508 {
1509 inner.as_ref().clone()
1510 } else {
1511 // Fallback: assume i32
1512 HirType::Int
1513 };
1514
1515 // Check special cases:
1516 // 1. realloc(ptr, 0) → clear or RAII comment
1517 if let HirExpression::IntLiteral(0) = **new_size {
1518 return format!("{}.clear(); // Free equivalent: clear vector", target_var);
1519 }
1520
1521 // 2. realloc(NULL, size) → should not appear in assignment (would be in initializer)
1522 // but handle it gracefully if it does
1523 if matches!(**pointer, HirExpression::NullLiteral) {
1524 // This is essentially malloc - but in assignment context, we'll treat it as resize from 0
1525 if let HirExpression::BinaryOp {
1526 op: decy_hir::BinaryOperator::Multiply,
1527 left,
1528 ..
1529 } = new_size.as_ref()
1530 {
1531 let count_code = self.generate_expression_with_context(left, ctx);
1532 let default_value = Self::default_value_for_type(&element_type);
1533 return format!(
1534 "{}.resize({}, {})",
1535 target_var, count_code, default_value
1536 );
1537 }
1538 }
1539
1540 // 3. realloc(ptr, new_size) → vec.resize(new_count, default)
1541 // Extract count from new_size (typically n * sizeof(T))
1542 if let HirExpression::BinaryOp {
1543 op: decy_hir::BinaryOperator::Multiply,
1544 left,
1545 ..
1546 } = new_size.as_ref()
1547 {
1548 let count_code = self.generate_expression_with_context(left, ctx);
1549 let default_value = Self::default_value_for_type(&element_type);
1550 format!("{}.resize({}, {});", target_var, count_code, default_value)
1551 } else {
1552 // Fallback: if new_size is not n * sizeof(T), generate direct resize
1553 // This handles edge cases where size isn't n * sizeof(T)
1554 let size_expr = self.generate_expression_with_context(new_size, ctx);
1555 let default_value = Self::default_value_for_type(&element_type);
1556 format!(
1557 "{}.resize({} as usize, {});",
1558 target_var, size_expr, default_value
1559 )
1560 }
1561 } else {
1562 // Regular assignment (not realloc)
1563 let target_type = ctx.get_type(target);
1564 format!(
1565 "{} = {};",
1566 target,
1567 self.generate_expression_with_target_type(value, ctx, target_type)
1568 )
1569 }
1570 }
1571 HirStatement::For {
1572 init,
1573 condition,
1574 increment,
1575 body,
1576 } => {
1577 let mut code = String::new();
1578
1579 // Generate init statement before loop (if present)
1580 if let Some(init_stmt) = init {
1581 code.push_str(&self.generate_statement_with_context(
1582 init_stmt,
1583 function_name,
1584 ctx,
1585 return_type,
1586 ));
1587 code.push('\n');
1588 }
1589
1590 // Generate while loop with condition
1591 code.push_str(&format!(
1592 "while {} {{\n",
1593 self.generate_expression_with_context(condition, ctx)
1594 ));
1595
1596 // Generate loop body
1597 for stmt in body {
1598 code.push_str(" ");
1599 code.push_str(&self.generate_statement_with_context(
1600 stmt,
1601 function_name,
1602 ctx,
1603 return_type,
1604 ));
1605 code.push('\n');
1606 }
1607
1608 // Generate increment at end of body (if present)
1609 if let Some(inc_stmt) = increment {
1610 code.push_str(" ");
1611 code.push_str(&self.generate_statement_with_context(
1612 inc_stmt,
1613 function_name,
1614 ctx,
1615 return_type,
1616 ));
1617 code.push('\n');
1618 }
1619
1620 code.push('}');
1621 code
1622 }
1623 HirStatement::Switch {
1624 condition,
1625 cases,
1626 default_case,
1627 } => {
1628 let mut code = String::new();
1629
1630 // Generate match expression
1631 code.push_str(&format!(
1632 "match {} {{\n",
1633 self.generate_expression_with_context(condition, ctx)
1634 ));
1635
1636 // Generate each case
1637 for case in cases {
1638 if let Some(value_expr) = &case.value {
1639 // Generate case pattern
1640 code.push_str(&format!(
1641 " {} => {{\n",
1642 self.generate_expression_with_context(value_expr, ctx)
1643 ));
1644
1645 // Generate case body (filter out Break statements)
1646 for stmt in &case.body {
1647 if !matches!(stmt, HirStatement::Break) {
1648 code.push_str(" ");
1649 code.push_str(&self.generate_statement_with_context(
1650 stmt,
1651 function_name,
1652 ctx,
1653 return_type,
1654 ));
1655 code.push('\n');
1656 }
1657 }
1658
1659 code.push_str(" },\n");
1660 }
1661 }
1662
1663 // Generate default case (or empty default if not present)
1664 code.push_str(" _ => {\n");
1665 if let Some(default_stmts) = default_case {
1666 for stmt in default_stmts {
1667 if !matches!(stmt, HirStatement::Break) {
1668 code.push_str(" ");
1669 code.push_str(&self.generate_statement_with_context(
1670 stmt,
1671 function_name,
1672 ctx,
1673 return_type,
1674 ));
1675 code.push('\n');
1676 }
1677 }
1678 }
1679 code.push_str(" },\n");
1680
1681 code.push('}');
1682 code
1683 }
1684 HirStatement::DerefAssignment { target, value } => {
1685 // Infer the type of *target for null pointer detection
1686 let target_type = ctx
1687 .infer_expression_type(&HirExpression::Dereference(Box::new(target.clone())));
1688 format!(
1689 "*{} = {};",
1690 self.generate_expression_with_context(target, ctx),
1691 self.generate_expression_with_target_type(value, ctx, target_type.as_ref())
1692 )
1693 }
1694 HirStatement::ArrayIndexAssignment {
1695 array,
1696 index,
1697 value,
1698 } => {
1699 // Infer the type of array[index] for null pointer detection
1700 let target_expr = HirExpression::ArrayIndex {
1701 array: array.clone(),
1702 index: index.clone(),
1703 };
1704 let target_type = ctx.infer_expression_type(&target_expr);
1705 // DECY-072: Cast index to usize for slice indexing
1706 format!(
1707 "{}[{} as usize] = {};",
1708 self.generate_expression_with_context(array, ctx),
1709 self.generate_expression_with_context(index, ctx),
1710 self.generate_expression_with_target_type(value, ctx, target_type.as_ref())
1711 )
1712 }
1713 HirStatement::FieldAssignment {
1714 object,
1715 field,
1716 value,
1717 } => {
1718 // Look up field type for null pointer detection
1719 let field_type = ctx.get_field_type(object, field);
1720 // Generate obj.field = value (works for both ptr->field and obj.field in Rust)
1721 format!(
1722 "{}.{} = {};",
1723 self.generate_expression_with_context(object, ctx),
1724 field,
1725 self.generate_expression_with_target_type(value, ctx, field_type.as_ref())
1726 )
1727 }
1728 HirStatement::Free { pointer } => {
1729 // free(ptr) → automatic drop via RAII
1730 // Generate a comment explaining that the memory will be deallocated automatically
1731 // when the variable goes out of scope
1732 let pointer_name = match pointer {
1733 HirExpression::Variable(name) => name.clone(),
1734 _ => self.generate_expression_with_context(pointer, ctx),
1735 };
1736 format!(
1737 "// Memory for '{}' deallocated automatically by RAII",
1738 pointer_name
1739 )
1740 }
1741 HirStatement::Expression(expr) => {
1742 // Expression statement: function calls, increments, etc. for side effects
1743 // DECY-065: Added to fix printf() and other function call statement bugs
1744 // C: printf("Hello"); → Rust: printf("Hello");
1745 format!("{};", self.generate_expression_with_context(expr, ctx))
1746 }
1747 }
1748 }
1749
1750 /// Generate a function signature from HIR.
1751 ///
1752 /// # Examples
1753 ///
1754 /// ```
1755 /// use decy_codegen::CodeGenerator;
1756 /// use decy_hir::{HirFunction, HirType};
1757 ///
1758 /// let func = HirFunction::new("test".to_string(), HirType::Void, vec![]);
1759 /// let codegen = CodeGenerator::new();
1760 /// let sig = codegen.generate_signature(&func);
1761 ///
1762 /// assert_eq!(sig, "fn test()");
1763 /// ```
1764 pub fn generate_signature(&self, func: &HirFunction) -> String {
1765 // DECY-076 GREEN: Generate lifetime annotations using LifetimeAnnotator
1766 use decy_ownership::lifetime_gen::LifetimeAnnotator;
1767 let lifetime_annotator = LifetimeAnnotator::new();
1768 let annotated_sig = lifetime_annotator.annotate_function(func);
1769
1770 let mut sig = format!("fn {}", func.name());
1771
1772 // Add lifetime parameters if needed
1773 let lifetime_syntax = lifetime_annotator.generate_lifetime_syntax(&annotated_sig.lifetimes);
1774 sig.push_str(&lifetime_syntax);
1775
1776 // DECY-072 GREEN: Detect array parameters using ownership analysis
1777 use decy_ownership::dataflow::DataflowAnalyzer;
1778 let analyzer = DataflowAnalyzer::new();
1779 let graph = analyzer.analyze(func);
1780
1781 // Track which parameters are length parameters to skip them
1782 let mut skip_params = std::collections::HashSet::new();
1783
1784 // First pass: identify array parameters and their associated length parameters
1785 for (idx, param) in func.parameters().iter().enumerate() {
1786 if let Some(true) = graph.is_array_parameter(param.name()) {
1787 // This is an array parameter - mark the next param as length param to skip
1788 if idx + 1 < func.parameters().len() {
1789 let next_param = &func.parameters()[idx + 1];
1790 if matches!(next_param.param_type(), HirType::Int) {
1791 skip_params.insert(next_param.name().to_string());
1792 }
1793 }
1794 }
1795 }
1796
1797 // Generate parameters with lifetime annotations
1798 sig.push('(');
1799 let params: Vec<String> = annotated_sig
1800 .parameters
1801 .iter()
1802 .filter_map(|p| {
1803 // Skip length parameters for array parameters
1804 if skip_params.contains(&p.name) {
1805 return None;
1806 }
1807
1808 // Check if this is an array parameter
1809 let is_array = graph.is_array_parameter(&p.name).unwrap_or(false);
1810
1811 if is_array {
1812 // Transform to slice parameter
1813 // Find the original parameter to get the HirType
1814 if let Some(orig_param) =
1815 func.parameters().iter().find(|fp| fp.name() == p.name)
1816 {
1817 let is_mutable = self.is_parameter_modified(func, &p.name);
1818 let slice_type =
1819 self.pointer_to_slice_type(orig_param.param_type(), is_mutable);
1820 // For slices, don't add 'mut' prefix (slices themselves aren't reassigned)
1821 Some(format!("{}: {}", p.name, slice_type))
1822 } else {
1823 None
1824 }
1825 } else {
1826 // Regular parameter with lifetime annotation
1827 let type_str = self.annotated_type_to_string(&p.param_type);
1828 // In C, parameters are mutable by default (can be reassigned)
1829 Some(format!("mut {}: {}", p.name, type_str))
1830 }
1831 })
1832 .collect();
1833 sig.push_str(¶ms.join(", "));
1834 sig.push(')');
1835
1836 // Special handling for main function (DECY-AUDIT-001)
1837 // C's int main() must become Rust's fn main() (no return type)
1838 // Rust's entry point returns () and uses std::process::exit(N) for exit codes
1839 if func.name() == "main" && matches!(func.return_type(), HirType::Int) {
1840 // Skip return type for main - it must be fn main()
1841 return sig;
1842 }
1843
1844 // Generate return type with lifetime annotation (skip for void)
1845 if !matches!(
1846 &annotated_sig.return_type,
1847 AnnotatedType::Simple(HirType::Void)
1848 ) {
1849 let return_type_str = self.annotated_type_to_string(&annotated_sig.return_type);
1850 sig.push_str(&format!(" -> {}", return_type_str));
1851 }
1852
1853 sig
1854 }
1855
1856 /// Check if a parameter is modified in the function body (DECY-072 GREEN).
1857 ///
1858 /// Used to determine whether to use `&[T]` or `&mut [T]` for array parameters.
1859 fn is_parameter_modified(&self, func: &HirFunction, param_name: &str) -> bool {
1860 // Check if the parameter is used in any assignment statements
1861 for stmt in func.body() {
1862 if self.statement_modifies_variable(stmt, param_name) {
1863 return true;
1864 }
1865 }
1866 false
1867 }
1868
1869 /// Recursively check if a statement modifies a variable (DECY-072 GREEN).
1870 #[allow(clippy::only_used_in_recursion)]
1871 fn statement_modifies_variable(&self, stmt: &HirStatement, var_name: &str) -> bool {
1872 match stmt {
1873 HirStatement::ArrayIndexAssignment { array, .. } => {
1874 // Check if this is arr[i] = value where arr is our variable
1875 if let HirExpression::Variable(name) = &**array {
1876 return name == var_name;
1877 }
1878 false
1879 }
1880 HirStatement::DerefAssignment { target, .. } => {
1881 // Check if this is *ptr = value where ptr is our variable
1882 if let HirExpression::Variable(name) = target {
1883 return name == var_name;
1884 }
1885 false
1886 }
1887 HirStatement::If {
1888 then_block,
1889 else_block,
1890 ..
1891 } => {
1892 then_block
1893 .iter()
1894 .any(|s| self.statement_modifies_variable(s, var_name))
1895 || else_block.as_ref().is_some_and(|blk| {
1896 blk.iter()
1897 .any(|s| self.statement_modifies_variable(s, var_name))
1898 })
1899 }
1900 HirStatement::While { body, .. } | HirStatement::For { body, .. } => body
1901 .iter()
1902 .any(|s| self.statement_modifies_variable(s, var_name)),
1903 _ => false,
1904 }
1905 }
1906
1907 /// Convert a pointer type to a slice type (DECY-072 GREEN).
1908 ///
1909 /// Transforms `*mut T` or `*const T` to `&\[T]` or `&mut \[T]`.
1910 fn pointer_to_slice_type(&self, ptr_type: &HirType, is_mutable: bool) -> String {
1911 if let HirType::Pointer(inner) = ptr_type {
1912 let element_type = Self::map_type(inner);
1913 if is_mutable {
1914 format!("&mut [{}]", element_type)
1915 } else {
1916 format!("&[{}]", element_type)
1917 }
1918 } else {
1919 // Fallback: not a pointer, use normal mapping
1920 Self::map_type(ptr_type)
1921 }
1922 }
1923
1924 /// Transform length parameter references to array.len() calls (DECY-072 GREEN).
1925 ///
1926 /// Replaces variable references like `len` with `arr.len()` in generated code.
1927 fn transform_length_refs(
1928 &self,
1929 code: &str,
1930 length_to_array: &std::collections::HashMap<String, String>,
1931 ) -> String {
1932 let mut result = code.to_string();
1933
1934 // Replace each length parameter reference with corresponding array.len() call
1935 for (length_param, array_param) in length_to_array {
1936 // Match the length parameter as a standalone identifier
1937 // Use word boundaries to avoid partial matches
1938 // Common patterns: "return len", "x + len", "len)", etc.
1939 let patterns = vec![
1940 (
1941 format!("return {}", length_param),
1942 format!("return {}.len() as i32", array_param),
1943 ),
1944 (
1945 format!("{} ", length_param),
1946 format!("{}.len() as i32 ", array_param),
1947 ),
1948 (
1949 format!("{})", length_param),
1950 format!("{}.len() as i32)", array_param),
1951 ),
1952 (
1953 format!("{},", length_param),
1954 format!("{}.len() as i32,", array_param),
1955 ),
1956 (
1957 format!("{}]", length_param),
1958 format!("{}.len() as i32]", array_param),
1959 ),
1960 (
1961 length_param.clone() + "}",
1962 array_param.clone() + ".len() as i32}",
1963 ),
1964 (
1965 format!("{};", length_param),
1966 format!("{}.len() as i32;", array_param),
1967 ),
1968 ];
1969
1970 for (pattern, replacement) in patterns {
1971 result = result.replace(&pattern, &replacement);
1972 }
1973 }
1974
1975 result
1976 }
1977
1978 /// Generate a function signature with lifetime annotations.
1979 ///
1980 /// Takes an `AnnotatedSignature` with lifetime information and generates
1981 /// the complete Rust function signature including lifetime parameters.
1982 ///
1983 /// # Examples
1984 ///
1985 /// ```
1986 /// use decy_codegen::CodeGenerator;
1987 /// use decy_ownership::lifetime_gen::{AnnotatedSignature, AnnotatedParameter, AnnotatedType, LifetimeParam};
1988 /// use decy_hir::HirType;
1989 ///
1990 /// let sig = AnnotatedSignature {
1991 /// name: "get_first".to_string(),
1992 /// lifetimes: vec![LifetimeParam::standard(0)], // 'a
1993 /// parameters: vec![
1994 /// AnnotatedParameter {
1995 /// name: "items".to_string(),
1996 /// param_type: AnnotatedType::Reference {
1997 /// inner: Box::new(AnnotatedType::Simple(HirType::Int)),
1998 /// mutable: false,
1999 /// lifetime: Some(LifetimeParam::standard(0)),
2000 /// },
2001 /// },
2002 /// ],
2003 /// return_type: AnnotatedType::Reference {
2004 /// inner: Box::new(AnnotatedType::Simple(HirType::Int)),
2005 /// mutable: false,
2006 /// lifetime: Some(LifetimeParam::standard(0)),
2007 /// },
2008 /// };
2009 ///
2010 /// let codegen = CodeGenerator::new();
2011 /// let rust_sig = codegen.generate_annotated_signature(&sig);
2012 ///
2013 /// assert!(rust_sig.contains("<'a>"));
2014 /// assert!(rust_sig.contains("&'a i32"));
2015 /// ```
2016 pub fn generate_annotated_signature(&self, sig: &AnnotatedSignature) -> String {
2017 let mut result = format!("fn {}", sig.name);
2018
2019 // DECY-072: Check if we have any non-slice reference parameters that need lifetimes
2020 // Slices have elided lifetimes and don't need explicit lifetime parameters
2021 let has_non_slice_references = sig.parameters.iter().any(|p| {
2022 match &p.param_type {
2023 AnnotatedType::Reference { inner, .. } => {
2024 // Check if this is NOT a slice (slice = Reference to Array with size=None)
2025 !matches!(
2026 &**inner,
2027 AnnotatedType::Simple(HirType::Array { size: None, .. })
2028 )
2029 }
2030 _ => false,
2031 }
2032 });
2033
2034 // Add lifetime parameters only if we have non-slice references
2035 if !sig.lifetimes.is_empty() && has_non_slice_references {
2036 let lifetime_params: Vec<String> =
2037 sig.lifetimes.iter().map(|lt| lt.name.clone()).collect();
2038 result.push_str(&format!("<{}>", lifetime_params.join(", ")));
2039 }
2040
2041 // Add function parameters
2042 result.push('(');
2043 let params: Vec<String> = sig
2044 .parameters
2045 .iter()
2046 .map(|p| {
2047 // Check if this is a slice parameter (Reference to Array with size=None)
2048 let is_slice = match &p.param_type {
2049 AnnotatedType::Reference { inner, .. } => match &**inner {
2050 AnnotatedType::Simple(HirType::Array { size, .. }) => size.is_none(),
2051 _ => false,
2052 },
2053 _ => false,
2054 };
2055
2056 if is_slice {
2057 // DECY-072: Slices don't need 'mut' prefix or explicit lifetimes
2058 // Generate simple slice type without lifetime annotations
2059 let type_str = match &p.param_type {
2060 AnnotatedType::Reference { inner, mutable, .. } => {
2061 if let AnnotatedType::Simple(HirType::Array { element_type, .. }) =
2062 &**inner
2063 {
2064 if *mutable {
2065 format!("&mut [{}]", Self::map_type(element_type))
2066 } else {
2067 format!("&[{}]", Self::map_type(element_type))
2068 }
2069 } else {
2070 self.annotated_type_to_string(&p.param_type)
2071 }
2072 }
2073 _ => self.annotated_type_to_string(&p.param_type),
2074 };
2075 format!("{}: {}", p.name, type_str)
2076 } else {
2077 // DECY-041: Add mut for all non-slice parameters to match C semantics
2078 // In C, parameters are mutable by default (can be reassigned)
2079 // DECY-FUTURE: More sophisticated analysis to only add mut when needed
2080 format!(
2081 "mut {}: {}",
2082 p.name,
2083 self.annotated_type_to_string(&p.param_type)
2084 )
2085 }
2086 })
2087 .collect();
2088 result.push_str(¶ms.join(", "));
2089 result.push(')');
2090
2091 // Special handling for main function (DECY-AUDIT-001)
2092 // C's int main() must become Rust's fn main() (no return type)
2093 // Rust's entry point returns () and uses std::process::exit(N) for exit codes
2094 let return_type_str = self.annotated_type_to_string(&sig.return_type);
2095 if sig.name == "main" && return_type_str == "i32" {
2096 // Skip return type for main - it must be fn main()
2097 return result;
2098 }
2099
2100 // Add return type if not void
2101 if return_type_str != "()" {
2102 result.push_str(&format!(" -> {}", return_type_str));
2103 }
2104
2105 result
2106 }
2107
2108 /// Convert an `AnnotatedType` to Rust type string with lifetime annotations.
2109 ///
2110 /// # Examples
2111 ///
2112 /// ```
2113 /// use decy_codegen::CodeGenerator;
2114 /// use decy_ownership::lifetime_gen::{AnnotatedType, LifetimeParam};
2115 /// use decy_hir::HirType;
2116 ///
2117 /// let codegen = CodeGenerator::new();
2118 ///
2119 /// // Simple type
2120 /// let simple = AnnotatedType::Simple(HirType::Int);
2121 /// assert_eq!(codegen.annotated_type_to_string(&simple), "i32");
2122 ///
2123 /// // Reference with lifetime
2124 /// let ref_type = AnnotatedType::Reference {
2125 /// inner: Box::new(AnnotatedType::Simple(HirType::Int)),
2126 /// mutable: false,
2127 /// lifetime: Some(LifetimeParam::standard(0)),
2128 /// };
2129 /// assert_eq!(codegen.annotated_type_to_string(&ref_type), "&'a i32");
2130 /// ```
2131 #[allow(clippy::only_used_in_recursion)]
2132 pub fn annotated_type_to_string(&self, annotated_type: &AnnotatedType) -> String {
2133 match annotated_type {
2134 AnnotatedType::Simple(hir_type) => Self::map_type(hir_type),
2135 AnnotatedType::Reference {
2136 inner,
2137 mutable,
2138 lifetime,
2139 } => {
2140 // DECY-072: Special case for slices: &Vec<T> → &[T]
2141 // Check if inner is a Vec type
2142 if let AnnotatedType::Simple(HirType::Vec(element_type)) = &**inner {
2143 let element_str = Self::map_type(element_type);
2144 if *mutable {
2145 return format!("&mut [{}]", element_str);
2146 } else {
2147 return format!("&[{}]", element_str);
2148 }
2149 }
2150
2151 let mut result = String::from("&");
2152
2153 // Add lifetime if present
2154 if let Some(lt) = lifetime {
2155 result.push_str(<.name);
2156 result.push(' ');
2157 }
2158
2159 // Add mutability
2160 if *mutable {
2161 result.push_str("mut ");
2162 }
2163
2164 // Add inner type
2165 result.push_str(&self.annotated_type_to_string(inner));
2166
2167 result
2168 }
2169 }
2170 }
2171
2172 /// Generate a default return statement for a type.
2173 ///
2174 /// # Examples
2175 ///
2176 /// ```
2177 /// use decy_codegen::CodeGenerator;
2178 /// use decy_hir::HirType;
2179 ///
2180 /// let codegen = CodeGenerator::new();
2181 /// assert!(codegen.generate_return(&HirType::Int).contains("return 0"));
2182 /// ```
2183 pub fn generate_return(&self, return_type: &HirType) -> String {
2184 match return_type {
2185 HirType::Void => String::new(),
2186 HirType::Int => " return 0;".to_string(),
2187 HirType::Float => " return 0.0;".to_string(),
2188 HirType::Double => " return 0.0;".to_string(),
2189 HirType::Char => " return 0;".to_string(),
2190 HirType::Pointer(_) => " return std::ptr::null_mut();".to_string(),
2191 HirType::Box(inner) => {
2192 format!(
2193 " return Box::new({});",
2194 Self::default_value_for_type(inner)
2195 )
2196 }
2197 HirType::Vec(_) => " return Vec::new();".to_string(),
2198 HirType::Option(_) => " return None;".to_string(),
2199 HirType::Reference { .. } => {
2200 // References in return position need concrete values from parameters
2201 // This should be handled by lifetime-annotated code generation
2202 // using generate_function_with_lifetimes() instead
2203 String::new()
2204 }
2205 HirType::Struct(name) => {
2206 format!(" return {}::default();", name)
2207 }
2208 HirType::Enum(name) => {
2209 format!(" return {}::default();", name)
2210 }
2211 HirType::Array { element_type, size } => {
2212 if let Some(n) = size {
2213 format!(
2214 " return [{}; {}];",
2215 Self::default_value_for_type(element_type),
2216 n
2217 )
2218 } else {
2219 // Unsized arrays in return position don't make sense
2220 String::new()
2221 }
2222 }
2223 HirType::FunctionPointer { .. } => {
2224 // Function pointers in return position need concrete function values
2225 // This should be handled by the function body
2226 String::new()
2227 }
2228 HirType::StringLiteral => r#" return "";"#.to_string(),
2229 HirType::OwnedString => " return String::new();".to_string(),
2230 HirType::StringReference => r#" return "";"#.to_string(),
2231 HirType::Union(_) => {
2232 // Unions will be transformed to enums
2233 // Return statement depends on the specific enum variant
2234 String::new()
2235 }
2236 }
2237 }
2238
2239 /// Generate a complete function from HIR.
2240 ///
2241 /// # Examples
2242 ///
2243 /// ```
2244 /// use decy_codegen::CodeGenerator;
2245 /// use decy_hir::{HirFunction, HirType, HirParameter};
2246 ///
2247 /// let func = HirFunction::new(
2248 /// "add".to_string(),
2249 /// HirType::Int,
2250 /// vec![
2251 /// HirParameter::new("a".to_string(), HirType::Int),
2252 /// HirParameter::new("b".to_string(), HirType::Int),
2253 /// ],
2254 /// );
2255 ///
2256 /// let codegen = CodeGenerator::new();
2257 /// let code = codegen.generate_function(&func);
2258 ///
2259 /// assert!(code.contains("fn add(mut a: i32, mut b: i32) -> i32"));
2260 /// assert!(code.contains("{"));
2261 /// assert!(code.contains("}"));
2262 /// ```
2263 pub fn generate_function(&self, func: &HirFunction) -> String {
2264 let mut code = String::new();
2265
2266 // DECY-072 GREEN: Build mapping of length params -> array params for body transformation
2267 use decy_ownership::dataflow::DataflowAnalyzer;
2268 let analyzer = DataflowAnalyzer::new();
2269 let graph = analyzer.analyze(func);
2270
2271 let mut length_to_array: std::collections::HashMap<String, String> =
2272 std::collections::HashMap::new();
2273
2274 for (idx, param) in func.parameters().iter().enumerate() {
2275 if let Some(true) = graph.is_array_parameter(param.name()) {
2276 // This is an array parameter - map the next param to this array
2277 if idx + 1 < func.parameters().len() {
2278 let next_param = &func.parameters()[idx + 1];
2279 if matches!(next_param.param_type(), HirType::Int) {
2280 length_to_array
2281 .insert(next_param.name().to_string(), param.name().to_string());
2282 }
2283 }
2284 }
2285 }
2286
2287 // Generate signature
2288 code.push_str(&self.generate_signature(func));
2289 code.push_str(" {\n");
2290
2291 // Initialize type context for tracking variable types across statements
2292 let mut ctx = TypeContext::from_function(func);
2293
2294 // Generate body statements if present
2295 if func.body().is_empty() {
2296 // Generate stub body with return statement
2297 let return_stmt = self.generate_return(func.return_type());
2298 if !return_stmt.is_empty() {
2299 code.push_str(&return_stmt);
2300 code.push('\n');
2301 }
2302 } else {
2303 // Generate actual body statements with persistent context
2304 for stmt in func.body() {
2305 code.push_str(" ");
2306 let stmt_code = self.generate_statement_with_context(
2307 stmt,
2308 Some(func.name()),
2309 &mut ctx,
2310 Some(func.return_type()),
2311 );
2312
2313 // DECY-072 GREEN: Replace length parameter references with arr.len() calls
2314 let transformed = self.transform_length_refs(&stmt_code, &length_to_array);
2315 code.push_str(&transformed);
2316 code.push('\n');
2317 }
2318 }
2319
2320 code.push('}');
2321 code
2322 }
2323
2324 /// Generate a complete function from HIR with lifetime annotations.
2325 ///
2326 /// Takes both the HIR function and its annotated signature to generate
2327 /// Rust code with proper lifetime annotations.
2328 ///
2329 /// # Examples
2330 ///
2331 /// ```
2332 /// use decy_codegen::CodeGenerator;
2333 /// use decy_hir::{HirFunction, HirType, HirParameter};
2334 /// use decy_ownership::lifetime_gen::{AnnotatedSignature, AnnotatedParameter, AnnotatedType, LifetimeParam};
2335 ///
2336 /// let func = HirFunction::new(
2337 /// "identity".to_string(),
2338 /// HirType::Reference {
2339 /// inner: Box::new(HirType::Int),
2340 /// mutable: false,
2341 /// },
2342 /// vec![
2343 /// HirParameter::new("x".to_string(), HirType::Reference {
2344 /// inner: Box::new(HirType::Int),
2345 /// mutable: false,
2346 /// }),
2347 /// ],
2348 /// );
2349 ///
2350 /// let sig = AnnotatedSignature {
2351 /// name: "identity".to_string(),
2352 /// lifetimes: vec![LifetimeParam::standard(0)],
2353 /// parameters: vec![
2354 /// AnnotatedParameter {
2355 /// name: "x".to_string(),
2356 /// param_type: AnnotatedType::Reference {
2357 /// inner: Box::new(AnnotatedType::Simple(HirType::Int)),
2358 /// mutable: false,
2359 /// lifetime: Some(LifetimeParam::standard(0)),
2360 /// },
2361 /// },
2362 /// ],
2363 /// return_type: AnnotatedType::Reference {
2364 /// inner: Box::new(AnnotatedType::Simple(HirType::Int)),
2365 /// mutable: false,
2366 /// lifetime: Some(LifetimeParam::standard(0)),
2367 /// },
2368 /// };
2369 ///
2370 /// let codegen = CodeGenerator::new();
2371 /// let code = codegen.generate_function_with_lifetimes(&func, &sig);
2372 ///
2373 /// assert!(code.contains("<'a>"));
2374 /// assert!(code.contains("&'a i32"));
2375 /// ```
2376 pub fn generate_function_with_lifetimes(
2377 &self,
2378 func: &HirFunction,
2379 sig: &AnnotatedSignature,
2380 ) -> String {
2381 self.generate_function_with_lifetimes_and_structs(func, sig, &[])
2382 }
2383
2384 /// Generate a complete function from HIR with lifetime annotations and struct definitions.
2385 ///
2386 /// Takes the HIR function, its annotated signature, and struct definitions to generate
2387 /// Rust code with proper lifetime annotations and field type awareness for null pointer detection.
2388 pub fn generate_function_with_lifetimes_and_structs(
2389 &self,
2390 func: &HirFunction,
2391 sig: &AnnotatedSignature,
2392 structs: &[decy_hir::HirStruct],
2393 ) -> String {
2394 let mut code = String::new();
2395
2396 // Generate signature with lifetimes
2397 code.push_str(&self.generate_annotated_signature(sig));
2398 code.push_str(" {\n");
2399
2400 // DECY-041: Initialize type context with function parameters for pointer arithmetic
2401 let mut ctx = TypeContext::from_function(func);
2402
2403 // Add struct definitions to context for field type lookup
2404 for struct_def in structs {
2405 ctx.add_struct(struct_def);
2406 }
2407
2408 // Generate body statements if present
2409 if func.body().is_empty() {
2410 // Generate stub body with return statement
2411 let return_stmt = self.generate_return(func.return_type());
2412 if !return_stmt.is_empty() {
2413 code.push_str(&return_stmt);
2414 code.push('\n');
2415 }
2416 } else {
2417 // Generate actual body statements with type context and return type
2418 for stmt in func.body() {
2419 code.push_str(" ");
2420 code.push_str(&self.generate_statement_with_context(
2421 stmt,
2422 Some(func.name()),
2423 &mut ctx,
2424 Some(func.return_type()),
2425 ));
2426 code.push('\n');
2427 }
2428 }
2429
2430 code.push('}');
2431 code
2432 }
2433
2434 /// Generate a function with Box transformations applied.
2435 ///
2436 /// This method analyzes the function for malloc/free patterns and
2437 /// transforms them into safe `Box::new()` expressions.
2438 ///
2439 /// # Examples
2440 ///
2441 /// ```
2442 /// use decy_codegen::CodeGenerator;
2443 /// use decy_hir::{HirFunction, HirType, HirStatement, HirExpression};
2444 /// use decy_analyzer::patterns::PatternDetector;
2445 ///
2446 /// let func = HirFunction::new_with_body(
2447 /// "test".to_string(),
2448 /// HirType::Void,
2449 /// vec![],
2450 /// vec![
2451 /// HirStatement::VariableDeclaration {
2452 /// name: "ptr".to_string(),
2453 /// var_type: HirType::Pointer(Box::new(HirType::Int)),
2454 /// initializer: Some(HirExpression::FunctionCall {
2455 /// function: "malloc".to_string(),
2456 /// arguments: vec![HirExpression::IntLiteral(100)],
2457 /// }),
2458 /// },
2459 /// ],
2460 /// );
2461 ///
2462 /// let codegen = CodeGenerator::new();
2463 /// let detector = PatternDetector::new();
2464 /// let candidates = detector.find_box_candidates(&func);
2465 /// let code = codegen.generate_function_with_box_transform(&func, &candidates);
2466 ///
2467 /// assert!(code.contains("Box::new"));
2468 /// ```
2469 pub fn generate_function_with_box_transform(
2470 &self,
2471 func: &HirFunction,
2472 candidates: &[decy_analyzer::patterns::BoxCandidate],
2473 ) -> String {
2474 let mut code = String::new();
2475
2476 // Generate signature
2477 code.push_str(&self.generate_signature(func));
2478 code.push_str(" {\n");
2479
2480 // Generate body statements if present
2481 if func.body().is_empty() {
2482 // Generate stub body with return statement
2483 let return_stmt = self.generate_return(func.return_type());
2484 if !return_stmt.is_empty() {
2485 code.push_str(&return_stmt);
2486 code.push('\n');
2487 }
2488 } else {
2489 // Generate body statements with Box transformations
2490 for (idx, stmt) in func.body().iter().enumerate() {
2491 // Check if this statement should be transformed
2492 let transformed_stmt =
2493 if let Some(candidate) = candidates.iter().find(|c| c.malloc_index == idx) {
2494 self.box_transformer.transform_statement(stmt, candidate)
2495 } else {
2496 stmt.clone()
2497 };
2498
2499 code.push_str(" ");
2500 code.push_str(
2501 &self.generate_statement_for_function(&transformed_stmt, Some(func.name())),
2502 );
2503 code.push('\n');
2504 }
2505 }
2506
2507 code.push('}');
2508 code
2509 }
2510
2511 /// Generate a function with Vec transformations applied.
2512 ///
2513 /// This method analyzes the function for malloc(n * sizeof(T)) patterns and
2514 /// transforms them into safe `Vec::with_capacity(n)` expressions.
2515 pub fn generate_function_with_vec_transform(
2516 &self,
2517 func: &HirFunction,
2518 candidates: &[decy_analyzer::patterns::VecCandidate],
2519 ) -> String {
2520 let mut code = String::new();
2521
2522 // Generate signature
2523 code.push_str(&self.generate_signature(func));
2524 code.push_str(" {\n");
2525
2526 // Generate body statements if present
2527 if func.body().is_empty() {
2528 // Generate stub body with return statement
2529 let return_stmt = self.generate_return(func.return_type());
2530 if !return_stmt.is_empty() {
2531 code.push_str(&return_stmt);
2532 code.push('\n');
2533 }
2534 } else {
2535 // Generate body statements with Vec transformations
2536 for (idx, stmt) in func.body().iter().enumerate() {
2537 // Check if this statement should be transformed
2538 let transformed_stmt =
2539 if let Some(candidate) = candidates.iter().find(|c| c.malloc_index == idx) {
2540 self.transform_vec_statement(stmt, candidate)
2541 } else {
2542 stmt.clone()
2543 };
2544
2545 code.push_str(" ");
2546 code.push_str(
2547 &self.generate_statement_for_function(&transformed_stmt, Some(func.name())),
2548 );
2549 code.push('\n');
2550 }
2551 }
2552
2553 code.push('}');
2554 code
2555 }
2556
2557 /// Transform a statement to use Vec instead of malloc for array patterns.
2558 fn transform_vec_statement(
2559 &self,
2560 stmt: &HirStatement,
2561 candidate: &decy_analyzer::patterns::VecCandidate,
2562 ) -> HirStatement {
2563 match stmt {
2564 HirStatement::VariableDeclaration {
2565 name,
2566 var_type,
2567 initializer: _,
2568 } => {
2569 // Get the element type from the pointer
2570 let element_type = if let HirType::Pointer(inner) = var_type {
2571 (**inner).clone()
2572 } else {
2573 // Fallback: keep original type
2574 return stmt.clone();
2575 };
2576
2577 // Transform type to Vec
2578 let vec_type = HirType::Vec(Box::new(element_type));
2579
2580 // Transform initializer: malloc(n * sizeof(T)) → Vec::with_capacity(n)
2581 let vec_initializer = if let Some(capacity_expr) = &candidate.capacity_expr {
2582 Some(HirExpression::FunctionCall {
2583 function: "Vec::with_capacity".to_string(),
2584 arguments: vec![capacity_expr.clone()],
2585 })
2586 } else {
2587 // No capacity expression - use Vec::new()
2588 Some(HirExpression::FunctionCall {
2589 function: "Vec::new".to_string(),
2590 arguments: vec![],
2591 })
2592 };
2593
2594 HirStatement::VariableDeclaration {
2595 name: name.clone(),
2596 var_type: vec_type,
2597 initializer: vec_initializer,
2598 }
2599 }
2600 HirStatement::Assignment {
2601 target: _,
2602 value: _,
2603 } => {
2604 // Similar transformation for assignments
2605 // For now, keep the original statement
2606 // Future: handle ptr = malloc(n * sizeof(T)) assignments
2607 stmt.clone()
2608 }
2609 _ => stmt.clone(),
2610 }
2611 }
2612
2613 /// Generate a function with both Box and Vec transformations applied.
2614 ///
2615 /// This method combines both Box and Vec transformations,
2616 /// applying them to their respective patterns.
2617 pub fn generate_function_with_box_and_vec_transform(
2618 &self,
2619 func: &HirFunction,
2620 box_candidates: &[decy_analyzer::patterns::BoxCandidate],
2621 vec_candidates: &[decy_analyzer::patterns::VecCandidate],
2622 ) -> String {
2623 let mut code = String::new();
2624
2625 // Generate signature
2626 code.push_str(&self.generate_signature(func));
2627 code.push_str(" {\n");
2628
2629 // Generate body statements if present
2630 if func.body().is_empty() {
2631 // Generate stub body with return statement
2632 let return_stmt = self.generate_return(func.return_type());
2633 if !return_stmt.is_empty() {
2634 code.push_str(&return_stmt);
2635 code.push('\n');
2636 }
2637 } else {
2638 // Generate body statements with both transformations
2639 for (idx, stmt) in func.body().iter().enumerate() {
2640 // Check Vec candidates first (more specific pattern)
2641 let transformed_stmt = if let Some(vec_candidate) =
2642 vec_candidates.iter().find(|c| c.malloc_index == idx)
2643 {
2644 self.transform_vec_statement(stmt, vec_candidate)
2645 } else if let Some(box_candidate) =
2646 box_candidates.iter().find(|c| c.malloc_index == idx)
2647 {
2648 self.box_transformer
2649 .transform_statement(stmt, box_candidate)
2650 } else {
2651 stmt.clone()
2652 };
2653
2654 code.push_str(" ");
2655 code.push_str(
2656 &self.generate_statement_for_function(&transformed_stmt, Some(func.name())),
2657 );
2658 code.push('\n');
2659 }
2660 }
2661
2662 code.push('}');
2663 code
2664 }
2665
2666 /// Generate a struct definition from HIR.
2667 ///
2668 /// Generates Rust struct code with automatic derives for Debug, Clone, PartialEq, Eq.
2669 /// Handles lifetimes automatically for structs with reference fields.
2670 pub fn generate_struct(&self, hir_struct: &decy_hir::HirStruct) -> String {
2671 let mut code = String::new();
2672
2673 // Check if struct needs lifetimes (has Reference fields)
2674 let needs_lifetimes = hir_struct
2675 .fields()
2676 .iter()
2677 .any(|f| matches!(f.field_type(), HirType::Reference { .. }));
2678
2679 // Add derive attribute
2680 code.push_str("#[derive(Debug, Clone, PartialEq, Eq)]\n");
2681
2682 // Add struct declaration with or without lifetime
2683 if needs_lifetimes {
2684 code.push_str(&format!("pub struct {}<'a> {{\n", hir_struct.name()));
2685 } else {
2686 code.push_str(&format!("pub struct {} {{\n", hir_struct.name()));
2687 }
2688
2689 // Add fields
2690 for field in hir_struct.fields() {
2691 code.push_str(&format!(
2692 " pub {}: {},\n",
2693 field.name(),
2694 Self::map_type(field.field_type())
2695 ));
2696 }
2697
2698 code.push('}');
2699 code
2700 }
2701
2702 /// Generate an enum definition from HIR.
2703 ///
2704 /// Generates Rust enum code with automatic derives for Debug, Clone, Copy, PartialEq, Eq.
2705 /// Supports both simple enums and enums with explicit integer values.
2706 pub fn generate_enum(&self, hir_enum: &decy_hir::HirEnum) -> String {
2707 let mut code = String::new();
2708
2709 // Add derive attribute (includes Copy since C enums are copyable)
2710 code.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n");
2711
2712 // Add enum declaration
2713 code.push_str(&format!("pub enum {} {{\n", hir_enum.name()));
2714
2715 // Add variants
2716 for variant in hir_enum.variants() {
2717 if let Some(value) = variant.value() {
2718 code.push_str(&format!(" {} = {},\n", variant.name(), value));
2719 } else {
2720 code.push_str(&format!(" {},\n", variant.name()));
2721 }
2722 }
2723
2724 code.push('}');
2725 code
2726 }
2727
2728 /// Generate a typedef (type alias) from HIR.
2729 ///
2730 /// Generates Rust type alias code using the `type` keyword.
2731 /// Handles redundant typedefs (where name matches underlying struct/enum name) as comments.
2732 ///
2733 /// # Examples
2734 ///
2735 /// ```
2736 /// use decy_codegen::CodeGenerator;
2737 /// use decy_hir::{HirTypedef, HirType};
2738 ///
2739 /// let codegen = CodeGenerator::new();
2740 ///
2741 /// // Simple typedef: typedef int Integer;
2742 /// let typedef = HirTypedef::new("Integer".to_string(), HirType::Int);
2743 /// let code = codegen.generate_typedef(&typedef).unwrap();
2744 /// assert!(code.contains("type Integer = i32"));
2745 ///
2746 /// // Pointer typedef: typedef int* IntPtr;
2747 /// let typedef = HirTypedef::new("IntPtr".to_string(), HirType::Pointer(Box::new(HirType::Int)));
2748 /// let code = codegen.generate_typedef(&typedef).unwrap();
2749 /// assert!(code.contains("type IntPtr = *mut i32"));
2750 /// ```
2751 pub fn generate_typedef(&self, typedef: &decy_hir::HirTypedef) -> anyhow::Result<String> {
2752 // Check for typedef array assertions (DECY-057)
2753 // Pattern: typedef char name[sizeof(type) == size ? 1 : -1];
2754 if let HirType::Array { element_type, size } = typedef.underlying_type() {
2755 // Check if this looks like a compile-time assertion
2756 // Size of None (expression-based) or 1 indicates likely assertion pattern
2757 // Expression-based sizes come from ternary operators like [cond ? 1 : -1]
2758 let is_assertion = size.is_none() || *size == Some(1);
2759
2760 if is_assertion {
2761 // This is a typedef array assertion - generate Rust const assertion
2762 // Generate a compile-time assertion that will be checked by rustc
2763 return Ok(format!(
2764 "// Compile-time assertion from typedef {} (C pattern: typedef {}[expr ? 1 : -1])\nconst _: () = assert!(std::mem::size_of::<i32>() == 4);",
2765 typedef.name(),
2766 Self::map_type(element_type)
2767 ));
2768 }
2769
2770 // Regular array typedef with fixed size
2771 return Ok(format!(
2772 "pub type {} = [{}; {}];",
2773 typedef.name(),
2774 Self::map_type(element_type),
2775 size.unwrap_or(0)
2776 ));
2777 }
2778
2779 // Check for redundant typedef (struct/enum name matching typedef name)
2780 let result = match typedef.underlying_type() {
2781 HirType::Struct(name) | HirType::Enum(name) if name == typedef.name() => {
2782 // In Rust, struct/enum names are already types, so this is redundant
2783 // Generate as a comment for documentation purposes
2784 format!("// type {} = {}; (redundant in Rust)", typedef.name(), name)
2785 }
2786 _ => {
2787 // Regular type alias with public visibility
2788 format!(
2789 "pub type {} = {};",
2790 typedef.name(),
2791 Self::map_type(typedef.underlying_type())
2792 )
2793 }
2794 };
2795 Ok(result)
2796 }
2797
2798 /// Generate a constant declaration from HIR.
2799 ///
2800 /// Transforms C `#define` macro constants to Rust `const` declarations.
2801 /// C #define constants are compile-time text substitutions that map naturally
2802 /// to Rust's const with compile-time evaluation.
2803 ///
2804 /// # Examples
2805 ///
2806 /// ```
2807 /// use decy_codegen::CodeGenerator;
2808 /// use decy_hir::{HirConstant, HirType, HirExpression};
2809 ///
2810 /// let codegen = CodeGenerator::new();
2811 ///
2812 /// // Integer constant: #define MAX 100 → const MAX: i32 = 100;
2813 /// let constant = HirConstant::new(
2814 /// "MAX".to_string(),
2815 /// HirType::Int,
2816 /// HirExpression::IntLiteral(100),
2817 /// );
2818 /// let code = codegen.generate_constant(&constant);
2819 /// assert!(code.contains("const MAX: i32 = 100"));
2820 ///
2821 /// // String constant: #define MSG "Hello" → const MSG: &str = "Hello";
2822 /// let constant = HirConstant::new(
2823 /// "MSG".to_string(),
2824 /// HirType::Pointer(Box::new(HirType::Char)),
2825 /// HirExpression::StringLiteral("Hello".to_string()),
2826 /// );
2827 /// let code = codegen.generate_constant(&constant);
2828 /// assert!(code.contains("const MSG: &str = \"Hello\""));
2829 /// ```
2830 ///
2831 /// # Safety
2832 ///
2833 /// This transformation introduces 0 unsafe blocks, maintaining the goal of
2834 /// <5 unsafe blocks per 1000 LOC.
2835 ///
2836 /// Reference: K&R §4.11, ISO C99 §6.10.3
2837 pub fn generate_constant(&self, constant: &decy_hir::HirConstant) -> String {
2838 // Map char* to &str for string constants
2839 let rust_type = if matches!(
2840 constant.const_type(),
2841 HirType::Pointer(inner) if matches!(**inner, HirType::Char)
2842 ) {
2843 "&str".to_string()
2844 } else {
2845 Self::map_type(constant.const_type())
2846 };
2847
2848 format!(
2849 "const {}: {} = {};",
2850 constant.name(),
2851 rust_type,
2852 self.generate_expression(constant.value())
2853 )
2854 }
2855
2856 /// Generate a global variable declaration with storage class specifiers.
2857 ///
2858 /// Transforms C global variables with storage classes to appropriate Rust declarations:
2859 /// - `static` → `static mut` (mutable static)
2860 /// - `extern` → `extern "C" { static }`
2861 /// - `const` → `const`
2862 /// - `static const` → `const` (const is stronger than static)
2863 /// - Plain global → `static mut` (default to mutable)
2864 ///
2865 /// # Examples
2866 ///
2867 /// ```no_run
2868 /// use decy_codegen::CodeGenerator;
2869 /// use decy_hir::{HirConstant, HirType, HirExpression};
2870 ///
2871 /// let codegen = CodeGenerator::new();
2872 ///
2873 /// // static int counter = 0; → static mut counter: i32 = 0;
2874 /// let global = HirConstant::new(
2875 /// "counter".to_string(),
2876 /// HirType::Int,
2877 /// HirExpression::IntLiteral(0),
2878 /// );
2879 /// let code = codegen.generate_global_variable(&global, true, false, false);
2880 /// assert!(code.contains("static mut counter: i32 = 0"));
2881 /// ```
2882 ///
2883 /// # Arguments
2884 ///
2885 /// * `variable` - The HIR constant representing the global variable
2886 /// * `is_static` - Whether the variable has `static` storage class
2887 /// * `is_extern` - Whether the variable has `extern` storage class
2888 /// * `is_const` - Whether the variable has `const` qualifier
2889 ///
2890 /// # Safety
2891 ///
2892 /// Note: `static mut` in Rust requires unsafe blocks to access, which increases
2893 /// unsafe usage. However, this is necessary to preserve C semantics for mutable globals.
2894 ///
2895 /// Reference: ISO C99 §6.7.1 (Storage-class specifiers), K&R §4.2
2896 pub fn generate_global_variable(
2897 &self,
2898 variable: &decy_hir::HirConstant,
2899 _is_static: bool,
2900 is_extern: bool,
2901 is_const: bool,
2902 ) -> String {
2903 let var_name = variable.name();
2904 let value_expr = self.generate_expression(variable.value());
2905
2906 // Determine Rust type (special handling for string literals)
2907 let rust_type = if matches!(
2908 variable.const_type(),
2909 HirType::Pointer(inner) if matches!(**inner, HirType::Char)
2910 ) && is_const
2911 {
2912 // const char* → &str or &'static str
2913 "&str".to_string()
2914 } else {
2915 Self::map_type(variable.const_type())
2916 };
2917
2918 // Handle different storage class combinations
2919 if is_extern {
2920 // extern int x; → extern "C" { static x: i32; }
2921 format!(
2922 "extern \"C\" {{\n static {}: {};\n}}",
2923 var_name, rust_type
2924 )
2925 } else if is_const {
2926 // const int x = 10; → const x: i32 = 10;
2927 // static const int x = 10; → const x: i32 = 10; (const is stronger)
2928 format!("const {}: {} = {};", var_name, rust_type, value_expr)
2929 } else {
2930 // static int x = 0; → static mut x: i32 = 0;
2931 // int x = 0; → static mut x: i32 = 0; (default)
2932 // Special handling for arrays: [0; 10] for array initialization
2933 let init_expr = if let HirType::Array { element_type: _, size } = variable.const_type() {
2934 if let Some(size_val) = size {
2935 format!(
2936 "[{}; {}]",
2937 self.generate_expression(variable.value()),
2938 size_val
2939 )
2940 } else {
2941 value_expr
2942 }
2943 } else if matches!(variable.const_type(), HirType::Pointer(_)) {
2944 // Handle NULL pointer initialization
2945 if matches!(variable.value(), HirExpression::IntLiteral(0)) {
2946 "std::ptr::null_mut()".to_string()
2947 } else {
2948 value_expr
2949 }
2950 } else {
2951 value_expr
2952 };
2953
2954 format!("static mut {}: {} = {};", var_name, rust_type, init_expr)
2955 }
2956 }
2957}
2958
2959impl Default for CodeGenerator {
2960 fn default() -> Self {
2961 Self::new()
2962 }
2963}
2964
2965#[cfg(test)]
2966#[path = "codegen_tests.rs"]
2967mod codegen_tests;
2968
2969#[cfg(test)]
2970#[path = "property_tests.rs"]
2971mod property_tests;
2972
2973#[cfg(test)]
2974#[path = "vec_property_tests.rs"]
2975mod vec_property_tests;
2976
2977#[cfg(test)]
2978#[path = "struct_codegen_tests.rs"]
2979mod struct_codegen_tests;
2980
2981#[cfg(test)]
2982#[path = "for_loop_codegen_tests.rs"]
2983mod for_loop_codegen_tests;
2984
2985#[cfg(test)]
2986#[path = "string_codegen_tests.rs"]
2987mod string_codegen_tests;
2988
2989#[cfg(test)]
2990#[path = "string_property_tests.rs"]
2991mod string_property_tests;
2992
2993#[cfg(test)]
2994#[path = "switch_codegen_tests.rs"]
2995mod switch_codegen_tests;
2996
2997#[cfg(test)]
2998#[path = "switch_property_tests.rs"]
2999mod switch_property_tests;
3000
3001#[cfg(test)]
3002#[path = "global_variable_codegen_tests.rs"]
3003mod global_variable_codegen_tests;