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