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