Skip to main content

decy_codegen/
stmt_gen.rs

1//! Statement code generation methods for CodeGenerator.
2//!
3//! Contains all methods related to generating Rust code from HIR statements,
4//! including declarations, assignments, control flow (if/while/for/switch),
5//! and pointer/array/field assignments.
6
7use super::{escape_rust_keyword, CodeGenerator, TypeContext};
8use decy_hir::{BinaryOperator, HirExpression, HirStatement, HirType};
9
10impl CodeGenerator {
11    /// Generate code for a statement.
12    pub fn generate_statement(&self, stmt: &HirStatement) -> String {
13        self.generate_statement_for_function(stmt, None)
14    }
15
16    /// Generate code for a statement, with optional function context.
17    ///
18    /// When function_name is "main", special handling applies (DECY-AUDIT-001):
19    /// - return N; becomes std::process::exit(N);
20    pub(crate) fn generate_statement_for_function(
21        &self,
22        stmt: &HirStatement,
23        function_name: Option<&str>,
24    ) -> String {
25        self.generate_statement_with_context(stmt, function_name, &mut TypeContext::new(), None)
26    }
27
28    /// Generate code for a statement with type context for pointer arithmetic and return type for null pointer detection.
29    pub(crate) fn generate_statement_with_context(
30        &self,
31        stmt: &HirStatement,
32        function_name: Option<&str>,
33        ctx: &mut TypeContext,
34        return_type: Option<&HirType>,
35    ) -> String {
36        match stmt {
37            HirStatement::VariableDeclaration { name, var_type, initializer } => {
38                self.generate_declaration_statement(name, var_type, initializer.as_ref(), ctx)
39            }
40            HirStatement::Return(expr_opt) => {
41                self.generate_return_statement(expr_opt.as_ref(), function_name, ctx, return_type)
42            }
43            HirStatement::If { condition, then_block, else_block } => {
44                self.generate_if_statement(
45                    condition,
46                    then_block,
47                    else_block.as_deref(),
48                    function_name,
49                    ctx,
50                    return_type,
51                )
52            }
53            HirStatement::While { condition, body } => {
54                self.generate_while_statement(
55                    condition,
56                    body,
57                    function_name,
58                    ctx,
59                    return_type,
60                )
61            }
62            HirStatement::Break => "break;".to_string(),
63            HirStatement::Continue => "continue;".to_string(),
64            HirStatement::Assignment { target, value } => {
65                self.generate_assignment_statement(target, value, ctx)
66            }
67            HirStatement::For { init, condition, increment, body } => {
68                self.generate_for_statement(
69                    init,
70                    condition.as_ref(),
71                    increment,
72                    body,
73                    function_name,
74                    ctx,
75                    return_type,
76                )
77            }
78            HirStatement::Switch { condition, cases, default_case } => {
79                self.generate_switch_statement(
80                    condition,
81                    cases,
82                    default_case.as_deref(),
83                    function_name,
84                    ctx,
85                    return_type,
86                )
87            }
88            HirStatement::DerefAssignment { target, value } => {
89                self.generate_deref_assignment_statement(target, value, ctx)
90            }
91            HirStatement::ArrayIndexAssignment { array, index, value } => {
92                self.generate_array_index_assignment_statement(array, index, value, ctx)
93            }
94            HirStatement::FieldAssignment { object, field, value } => {
95                self.generate_field_assignment_statement(object, field, value, ctx)
96            }
97            HirStatement::Free { pointer } => {
98                let pointer_name = match pointer {
99                    HirExpression::Variable(name) => name.clone(),
100                    _ => self.generate_expression_with_context(pointer, ctx),
101                };
102                format!("// Memory for '{}' deallocated automatically by RAII", pointer_name)
103            }
104            HirStatement::Expression(expr) => {
105                format!("{};", self.generate_expression_with_context(expr, ctx))
106            }
107            HirStatement::InlineAsm { text, translatable } => {
108                let mut result = String::new();
109                result.push_str("// DECY: manual review required - inline assembly\n");
110                if *translatable {
111                    result.push_str(
112                        "// DECY: this assembly may be translatable to Rust intrinsics\n",
113                    );
114                }
115                result.push_str(&format!("// Original asm: {}", text.replace('\n', "\n// ")));
116                result
117            }
118        }
119    }
120
121    /// Generate a variable declaration statement.
122    fn resolve_declaration_type(
123        name: &str,
124        var_type: &HirType,
125        initializer: Option<&HirExpression>,
126        is_malloc_init: bool,
127        ctx: &mut TypeContext,
128    ) -> (HirType, String) {
129        if is_malloc_init {
130            if let HirType::Pointer(inner) = var_type {
131                let is_struct_alloc = matches!(&**inner, HirType::Struct(_));
132                let is_array_pattern = if let Some(init_expr) = initializer {
133                    Self::is_malloc_array_pattern(init_expr)
134                } else {
135                    false
136                };
137
138                if is_struct_alloc && !is_array_pattern {
139                    let box_type = HirType::Box(inner.clone());
140                    ctx.add_variable(name.to_string(), box_type.clone());
141                    return (box_type.clone(), Self::map_type(&box_type));
142                } else {
143                    let vec_type = HirType::Vec(inner.clone());
144                    ctx.add_variable(name.to_string(), vec_type.clone());
145                    return (vec_type.clone(), Self::map_type(&vec_type));
146                }
147            } else {
148                ctx.add_variable(name.to_string(), var_type.clone());
149                return (var_type.clone(), Self::map_type(var_type));
150            }
151        }
152
153        let is_string_literal_init =
154            matches!(initializer, Some(HirExpression::StringLiteral(_)));
155        let is_char_pointer = matches!(
156            var_type,
157            HirType::Pointer(inner) if matches!(&**inner, HirType::Char)
158        );
159        let is_char_pointer_array = matches!(
160            var_type,
161            HirType::Array { element_type, .. }
162            if matches!(&**element_type, HirType::Pointer(inner) if matches!(&**inner, HirType::Char))
163        );
164        let is_array_of_string_literals = matches!(
165            initializer,
166            Some(HirExpression::CompoundLiteral { initializers, .. })
167            if initializers.iter().all(|e| matches!(e, HirExpression::StringLiteral(_)))
168        );
169
170        if is_char_pointer && is_string_literal_init {
171            ctx.add_variable(name.to_string(), HirType::StringReference);
172            (HirType::StringReference, "&str".to_string())
173        } else if is_char_pointer_array && is_array_of_string_literals {
174            let size =
175                if let HirType::Array { size, .. } = var_type { *size } else { None };
176            let array_type = HirType::Array {
177                element_type: Box::new(HirType::StringReference),
178                size,
179            };
180            ctx.add_variable(name.to_string(), array_type.clone());
181            let type_str = if let Some(n) = size {
182                format!("[&str; {}]", n)
183            } else {
184                "[&str]".to_string()
185            };
186            (array_type, type_str)
187        } else {
188            ctx.add_variable(name.to_string(), var_type.clone());
189            (var_type.clone(), Self::map_type(var_type))
190        }
191    }
192
193    fn generate_malloc_expr_init(
194        &self,
195        code: &mut String,
196        var_type: &HirType,
197        init_expr: &HirExpression,
198        ctx: &TypeContext,
199    ) {
200        match var_type {
201            HirType::Box(inner) => {
202                code.push_str(&format!(
203                    " = Box::new({});",
204                    Self::default_value_for_type(inner)
205                ));
206            }
207            HirType::Vec(_) => {
208                if let HirExpression::Malloc { size } = init_expr {
209                    if let HirExpression::BinaryOp {
210                        op: decy_hir::BinaryOperator::Multiply,
211                        left,
212                        ..
213                    } = size.as_ref()
214                    {
215                        let capacity_code =
216                            self.generate_expression_with_context(left, ctx);
217                        code.push_str(&format!(
218                            " = Vec::with_capacity({});",
219                            capacity_code
220                        ));
221                    } else {
222                        code.push_str(" = Vec::new();");
223                    }
224                } else {
225                    code.push_str(" = Vec::new();");
226                }
227            }
228            _ => {
229                code.push_str(" = Box::new(0i32);");
230            }
231        }
232    }
233
234    fn generate_malloc_funcall_init(
235        &self,
236        code: &mut String,
237        var_type: &HirType,
238        actual_type: &HirType,
239        init_expr: &HirExpression,
240        ctx: &mut TypeContext,
241    ) {
242        match actual_type {
243            HirType::Box(inner) => {
244                let use_default =
245                    if let HirType::Struct(struct_name) = inner.as_ref() {
246                        ctx.struct_has_default(struct_name)
247                    } else {
248                        false
249                    };
250
251                if use_default {
252                    code.push_str(" = Box::default();");
253                } else {
254                    let inner_type = Self::map_type(inner);
255                    code.push_str(&format!(
256                        " = Box::new(/* SAFETY: {} is valid when zero-initialized */ unsafe {{ std::mem::zeroed::<{}>() }});",
257                        inner_type, inner_type
258                    ));
259                }
260            }
261            HirType::Vec(_) => {
262                code.push_str(&format!(
263                    " = {};",
264                    self.generate_expression_with_target_type(
265                        init_expr,
266                        ctx,
267                        Some(actual_type)
268                    )
269                ));
270            }
271            _ => {
272                code.push_str(&format!(
273                    " = {};",
274                    self.generate_expression_with_target_type(
275                        init_expr,
276                        ctx,
277                        Some(var_type)
278                    )
279                ));
280            }
281        }
282    }
283
284    fn generate_regular_init(
285        &self,
286        code: &mut String,
287        var_type: &HirType,
288        actual_type: &HirType,
289        init_expr: &HirExpression,
290        ctx: &mut TypeContext,
291    ) {
292        let is_char_array = matches!(
293            var_type,
294            HirType::Array { element_type, .. }
295            if matches!(&**element_type, HirType::Char)
296        );
297
298        if is_char_array {
299            if let HirExpression::StringLiteral(s) = init_expr {
300                let escaped: String = s
301                    .chars()
302                    .map(|c| match c {
303                        '"' => "\\\"".to_string(),
304                        c => c.to_string(),
305                    })
306                    .collect();
307                code.push_str(&format!(" = *b\"{}\\0\";", escaped));
308            } else {
309                code.push_str(&format!(
310                    " = {};",
311                    self.generate_expression_with_target_type(
312                        init_expr,
313                        ctx,
314                        Some(var_type)
315                    )
316                ));
317            }
318        } else {
319            code.push_str(&format!(
320                " = {};",
321                self.generate_expression_with_target_type(
322                    init_expr,
323                    ctx,
324                    Some(actual_type)
325                )
326            ));
327        }
328    }
329
330    fn generate_declaration_statement(
331        &self,
332        name: &str,
333        var_type: &HirType,
334        initializer: Option<&HirExpression>,
335        ctx: &mut TypeContext,
336    ) -> String {
337        let escaped_name = escape_rust_keyword(name);
338        let escaped_name = if ctx.is_global(&escaped_name) {
339            let renamed = format!("{}_local", escaped_name);
340            ctx.add_renamed_local(escaped_name.clone(), renamed.clone());
341            renamed
342        } else {
343            escaped_name
344        };
345        if let HirType::Array { element_type, size: None } = var_type {
346            if let Some(size_expr) = initializer {
347                let size_code = self.generate_expression_with_context(size_expr, ctx);
348                let default_value = match element_type.as_ref() {
349                    HirType::Int => "0i32",
350                    HirType::UnsignedInt => "0u32",
351                    HirType::Float => "0.0f32",
352                    HirType::Double => "0.0f64",
353                    HirType::Char => "0u8",
354                    HirType::SignedChar => "0i8",
355                    _ => &Self::default_value_for_type(element_type),
356                };
357
358                ctx.add_variable(
359                    name.to_string(),
360                    HirType::Vec(Box::new(element_type.as_ref().clone())),
361                );
362
363                return format!(
364                    "let mut {} = vec![{}; {}];",
365                    escaped_name, default_value, size_code
366                );
367            }
368        }
369
370        let is_malloc_init = if let Some(init_expr) = initializer {
371            Self::is_any_malloc_or_calloc(init_expr)
372        } else {
373            false
374        };
375
376        let (actual_type, type_str) =
377            Self::resolve_declaration_type(name, var_type, initializer, is_malloc_init, ctx);
378
379        let mutability = "mut ";
380        let mut code = format!("let {}{}: {}", mutability, escaped_name, type_str);
381        if let Some(init_expr) = initializer {
382            if matches!(init_expr, HirExpression::Malloc { .. }) {
383                self.generate_malloc_expr_init(&mut code, var_type, init_expr, ctx);
384            } else if is_malloc_init {
385                self.generate_malloc_funcall_init(
386                    &mut code, var_type, &actual_type, init_expr, ctx,
387                );
388            } else {
389                self.generate_regular_init(
390                    &mut code, var_type, &actual_type, init_expr, ctx,
391                );
392            }
393        } else {
394            code.push_str(&format!(" = {};", Self::default_value_for_type(var_type)));
395        }
396        code
397    }
398
399    /// Generate a return statement.
400    fn generate_return_statement(
401        &self,
402        expr_opt: Option<&HirExpression>,
403        function_name: Option<&str>,
404        ctx: &mut TypeContext,
405        return_type: Option<&HirType>,
406    ) -> String {
407        // Special handling for main function (DECY-AUDIT-001)
408        // return N; in main becomes std::process::exit(N);
409        if function_name == Some("main") {
410            if let Some(expr) = expr_opt {
411                let expr_code = self.generate_expression_with_context(expr, ctx);
412                // DECY-126: Check if expression type needs cast to i32
413                // exit() expects i32, but char/u8 expressions need casting
414                let expr_type = ctx.infer_expression_type(expr);
415                let needs_cast = matches!(expr_type, Some(HirType::Char));
416                if needs_cast {
417                    format!("std::process::exit({} as i32);", expr_code)
418                } else {
419                    format!("std::process::exit({});", expr_code)
420                }
421            } else {
422                "std::process::exit(0);".to_string()
423            }
424        } else if let Some(expr) = expr_opt {
425            // Pass return type as target type hint for null pointer detection
426            format!(
427                "return {};",
428                self.generate_expression_with_target_type(expr, ctx, return_type)
429            )
430        } else {
431            "return;".to_string()
432        }
433    }
434
435    /// Generate an if statement.
436    fn generate_if_statement(
437        &self,
438        condition: &HirExpression,
439        then_block: &[HirStatement],
440        else_block: Option<&[HirStatement]>,
441        function_name: Option<&str>,
442        ctx: &mut TypeContext,
443        return_type: Option<&HirType>,
444    ) -> String {
445        let mut code = String::new();
446
447        // Generate if condition
448        // DECY-131: If condition is not already boolean, wrap appropriately
449        let cond_code = self.generate_expression_with_context(condition, ctx);
450        let cond_str = if Self::is_boolean_expression(condition) {
451            cond_code
452        } else {
453            // DECY-238: Check if condition is a pointer type - use !ptr.is_null()
454            if let Some(cond_type) = ctx.infer_expression_type(condition) {
455                if matches!(cond_type, HirType::Pointer(_)) {
456                    format!("!{}.is_null()", cond_code)
457                } else {
458                    format!("({}) != 0", cond_code)
459                }
460            } else {
461                format!("({}) != 0", cond_code)
462            }
463        };
464        code.push_str(&format!("if {} {{\n", cond_str));
465
466        // Generate then block
467        for stmt in then_block {
468            code.push_str("    ");
469            code.push_str(&self.generate_statement_with_context(
470                stmt,
471                function_name,
472                ctx,
473                return_type,
474            ));
475            code.push('\n');
476        }
477
478        // Generate else block if present
479        if let Some(else_stmts) = else_block {
480            code.push_str("} else {\n");
481            for stmt in else_stmts {
482                code.push_str("    ");
483                code.push_str(&self.generate_statement_with_context(
484                    stmt,
485                    function_name,
486                    ctx,
487                    return_type,
488                ));
489                code.push('\n');
490            }
491        }
492
493        code.push('}');
494        code
495    }
496
497    /// Generate a while statement.
498    fn generate_while_statement(
499        &self,
500        condition: &HirExpression,
501        body: &[HirStatement],
502        function_name: Option<&str>,
503        ctx: &mut TypeContext,
504        return_type: Option<&HirType>,
505    ) -> String {
506        let mut code = String::new();
507
508        // Generate while condition
509        // DECY-138: Check for string iteration pattern: while (*str) → while !str.is_empty()
510        let cond_str = if let Some(str_var) = Self::get_string_deref_var(condition, ctx) {
511            format!("!{}.is_empty()", str_var)
512        } else {
513            // DECY-123: If condition is not already boolean, wrap appropriately
514            let cond_code = self.generate_expression_with_context(condition, ctx);
515            if Self::is_boolean_expression(condition) {
516                cond_code
517            } else {
518                // DECY-238: Check if condition is a pointer type - use !ptr.is_null()
519                if let Some(cond_type) = ctx.infer_expression_type(condition) {
520                    if matches!(cond_type, HirType::Pointer(_)) {
521                        format!("!{}.is_null()", cond_code)
522                    } else {
523                        format!("({}) != 0", cond_code)
524                    }
525                } else {
526                    format!("({}) != 0", cond_code)
527                }
528            }
529        };
530        code.push_str(&format!("while {} {{\n", cond_str));
531
532        // Generate loop body
533        for stmt in body {
534            code.push_str("    ");
535            code.push_str(&self.generate_statement_with_context(
536                stmt,
537                function_name,
538                ctx,
539                return_type,
540            ));
541            code.push('\n');
542        }
543
544        code.push('}');
545        code
546    }
547
548    /// Generate an assignment statement (including realloc handling).
549    fn generate_assignment_statement(
550        &self,
551        target: &str,
552        value: &HirExpression,
553        ctx: &mut TypeContext,
554    ) -> String {
555        // Special handling for realloc() → Vec::resize/truncate/clear
556        if let HirExpression::Realloc { pointer, new_size } = value {
557            // target is a String (variable name) in Assignment statements
558            let target_var = target.to_string();
559
560            // Check if target is a Vec type to get element type
561            let element_type = if let Some(HirType::Vec(inner)) = ctx.get_type(&target_var)
562            {
563                inner.as_ref().clone()
564            } else {
565                // Fallback: assume i32
566                HirType::Int
567            };
568
569            // Check special cases:
570            // 1. realloc(ptr, 0) → clear or RAII comment
571            if let HirExpression::IntLiteral(0) = **new_size {
572                return format!("{}.clear(); // Free equivalent: clear vector", target_var);
573            }
574
575            // 2. realloc(NULL, size) → should not appear in assignment (would be in initializer)
576            //    but handle it gracefully if it does
577            if matches!(**pointer, HirExpression::NullLiteral) {
578                // This is essentially malloc - but in assignment context, we'll treat it as resize from 0
579                if let HirExpression::BinaryOp {
580                    op: decy_hir::BinaryOperator::Multiply,
581                    left,
582                    ..
583                } = new_size.as_ref()
584                {
585                    let count_code = self.generate_expression_with_context(left, ctx);
586                    let default_value = Self::default_value_for_type(&element_type);
587                    return format!(
588                        "{}.resize({}, {})",
589                        target_var, count_code, default_value
590                    );
591                }
592            }
593
594            // 3. realloc(ptr, new_size) → vec.resize(new_count, default)
595            // Extract count from new_size (typically n * sizeof(T))
596            if let HirExpression::BinaryOp {
597                op: decy_hir::BinaryOperator::Multiply,
598                left,
599                ..
600            } = new_size.as_ref()
601            {
602                let count_code = self.generate_expression_with_context(left, ctx);
603                let default_value = Self::default_value_for_type(&element_type);
604                format!("{}.resize({}, {});", target_var, count_code, default_value)
605            } else {
606                // Fallback: if new_size is not n * sizeof(T), generate direct resize
607                // This handles edge cases where size isn't n * sizeof(T)
608                let size_expr = self.generate_expression_with_context(new_size, ctx);
609                let default_value = Self::default_value_for_type(&element_type);
610                format!("{}.resize({} as usize, {});", target_var, size_expr, default_value)
611            }
612        } else {
613            // DECY-134: Check for string iteration param pointer arithmetic
614            // ptr = ptr + 1 → ptr_idx += 1
615            if let Some(idx_var) = ctx.get_string_iter_index(target) {
616                // Check if this is ptr = ptr + N or ptr = ptr - N
617                if let HirExpression::BinaryOp { op, left, right } = value {
618                    if let HirExpression::Variable(var_name) = &**left {
619                        if var_name == target {
620                            let right_code =
621                                self.generate_expression_with_context(right, ctx);
622                            return match op {
623                                BinaryOperator::Add => {
624                                    format!("{} += {} as usize;", idx_var, right_code)
625                                }
626                                BinaryOperator::Subtract => {
627                                    format!("{} -= {} as usize;", idx_var, right_code)
628                                }
629                                _ => format!(
630                                    "{} = {};",
631                                    target,
632                                    self.generate_expression_with_context(value, ctx)
633                                ),
634                            };
635                        }
636                    }
637                }
638            }
639            // Regular assignment (not realloc)
640            let target_type = ctx.get_type(target);
641            let value_code =
642                self.generate_expression_with_target_type(value, ctx, target_type);
643
644            // DECY-261: Helper to strip nested unsafe blocks to avoid redundancy
645            fn strip_nested_unsafe(code: &str) -> String {
646                // Iteratively strip all unsafe { } wrappers from the code
647                let mut result = code.to_string();
648                while result.contains("unsafe { ") {
649                    result = result.replace("unsafe { ", "").replace(" }", "");
650                    // Handle case where there might be multiple closings
651                    // Simple approach: strip pattern "unsafe { X }" → "X"
652                }
653                // More precise: use regex-like matching for simple cases
654                result = code.replace("unsafe { ", "").replacen(
655                    " }",
656                    "",
657                    code.matches("unsafe { ").count(),
658                );
659                result
660            }
661
662            // DECY-241: Handle errno assignment specially
663            if target == "errno" {
664                let clean_value = strip_nested_unsafe(&value_code);
665                return format!("unsafe {{ ERRNO = {}; }}", clean_value);
666            }
667            // DECY-220: Wrap global variable assignment in unsafe block
668            // DECY-261: Strip nested unsafe from value_code to avoid redundancy
669            if ctx.is_global(target) {
670                let clean_value = strip_nested_unsafe(&value_code);
671                format!("unsafe {{ {} = {}; }}", target, clean_value)
672            } else {
673                format!("{} = {};", target, value_code)
674            }
675        }
676    }
677
678    /// Generate a for statement.
679    fn generate_for_statement(
680        &self,
681        init: &[HirStatement],
682        condition: Option<&HirExpression>,
683        increment: &[HirStatement],
684        body: &[HirStatement],
685        function_name: Option<&str>,
686        ctx: &mut TypeContext,
687        return_type: Option<&HirType>,
688    ) -> String {
689        let mut code = String::new();
690
691        // DECY-224: Generate ALL init statements before loop
692        for init_stmt in init {
693            code.push_str(&self.generate_statement_with_context(
694                init_stmt,
695                function_name,
696                ctx,
697                return_type,
698            ));
699            code.push('\n');
700        }
701
702        // Generate loop: `loop {}` for None (for(;;)), `while cond {}` for Some
703        if let Some(cond) = condition {
704            code.push_str(&format!(
705                "while {} {{\n",
706                self.generate_expression_with_context(cond, ctx)
707            ));
708        } else {
709            code.push_str("loop {\n");
710        }
711
712        // Generate loop body
713        for stmt in body {
714            code.push_str("    ");
715            code.push_str(&self.generate_statement_with_context(
716                stmt,
717                function_name,
718                ctx,
719                return_type,
720            ));
721            code.push('\n');
722        }
723
724        // DECY-224: Generate ALL increment statements at end of body
725        for inc_stmt in increment {
726            code.push_str("    ");
727            code.push_str(&self.generate_statement_with_context(
728                inc_stmt,
729                function_name,
730                ctx,
731                return_type,
732            ));
733            code.push('\n');
734        }
735
736        code.push('}');
737        code
738    }
739
740    /// Generate a switch statement as a Rust match expression.
741    fn generate_switch_statement(
742        &self,
743        condition: &HirExpression,
744        cases: &[decy_hir::SwitchCase],
745        default_case: Option<&[HirStatement]>,
746        function_name: Option<&str>,
747        ctx: &mut TypeContext,
748        return_type: Option<&HirType>,
749    ) -> String {
750        let mut code = String::new();
751
752        // Generate match expression
753        code.push_str(&format!(
754            "match {} {{\n",
755            self.generate_expression_with_context(condition, ctx)
756        ));
757
758        // DECY-209: Infer switch condition type for case pattern matching
759        let condition_type = ctx.infer_expression_type(condition);
760        let condition_is_int = matches!(condition_type, Some(HirType::Int));
761
762        // Generate each case
763        for case in cases {
764            if let Some(value_expr) = &case.value {
765                // Generate case pattern
766                // DECY-209/DECY-219: If condition is Int and case is CharLiteral,
767                // generate the numeric byte value directly as the pattern.
768                // Rust match patterns don't allow casts like `b'0' as i32`,
769                // so we must use the numeric value (e.g., 48 for '0')
770                let case_pattern = if condition_is_int {
771                    if let HirExpression::CharLiteral(ch) = value_expr {
772                        // Direct numeric value for the character
773                        format!("{}", (*ch) as i32)
774                    } else {
775                        self.generate_expression_with_context(value_expr, ctx)
776                    }
777                } else {
778                    self.generate_expression_with_context(value_expr, ctx)
779                };
780                code.push_str(&format!("    {} => {{\n", case_pattern));
781
782                // Generate case body (filter out Break statements)
783                for stmt in &case.body {
784                    if !matches!(stmt, HirStatement::Break) {
785                        code.push_str("        ");
786                        code.push_str(&self.generate_statement_with_context(
787                            stmt,
788                            function_name,
789                            ctx,
790                            return_type,
791                        ));
792                        code.push('\n');
793                    }
794                }
795
796                code.push_str("    },\n");
797            }
798        }
799
800        // Generate default case (or empty default if not present)
801        code.push_str("    _ => {\n");
802        if let Some(default_stmts) = default_case {
803            for stmt in default_stmts {
804                if !matches!(stmt, HirStatement::Break) {
805                    code.push_str("        ");
806                    code.push_str(&self.generate_statement_with_context(
807                        stmt,
808                        function_name,
809                        ctx,
810                        return_type,
811                    ));
812                    code.push('\n');
813                }
814            }
815        }
816        code.push_str("    },\n");
817
818        code.push('}');
819        code
820    }
821
822    /// Generate a dereference assignment statement.
823    fn generate_deref_assignment_statement(
824        &self,
825        target: &HirExpression,
826        value: &HirExpression,
827        ctx: &mut TypeContext,
828    ) -> String {
829        // DECY-185: Handle struct field access targets directly (no dereference needed)
830        // sb->capacity = value should generate (*sb).capacity = value, not *(*sb).capacity = value
831        // DECY-254: ArrayIndex also doesn't need extra dereference
832        // arr[i] *= 2 should generate arr[(i) as usize] = arr[(i) as usize] * 2
833        if matches!(
834            target,
835            HirExpression::PointerFieldAccess { .. }
836                | HirExpression::FieldAccess { .. }
837                | HirExpression::ArrayIndex { .. }
838        ) {
839            let target_code = self.generate_expression_with_context(target, ctx);
840            let value_code = self.generate_expression_with_context(value, ctx);
841            return format!("{} = {};", target_code, value_code);
842        }
843
844        // DECY-134: Check for string iteration param - use slice indexing
845        if let HirExpression::Variable(var_name) = target {
846            if let Some(idx_var) = ctx.get_string_iter_index(var_name) {
847                // Transform *ptr = value to slice[idx] = value - no unsafe needed!
848                let value_code = self.generate_expression_with_context(value, ctx);
849                return format!("{}[{}] = {};", var_name, idx_var, value_code);
850            }
851        }
852
853        // Infer the type of *target for null pointer detection
854        let target_type = ctx
855            .infer_expression_type(&HirExpression::Dereference(Box::new(target.clone())));
856        let target_code = self.generate_expression_with_context(target, ctx);
857        let value_code =
858            self.generate_expression_with_target_type(value, ctx, target_type.as_ref());
859
860        // Helper to strip nested unsafe blocks - returns owned String to avoid lifetime issues
861        fn strip_unsafe(code: &str) -> String {
862            if code.starts_with("unsafe { ") && code.ends_with(" }") {
863                code.strip_prefix("unsafe { ")
864                    .and_then(|s| s.strip_suffix(" }"))
865                    .unwrap_or(code)
866                    .to_string()
867            } else {
868                code.to_string()
869            }
870        }
871
872        // DECY-124: Check if target is a raw pointer - if so, wrap in unsafe
873        if let HirExpression::Variable(var_name) = target {
874            if ctx.is_pointer(var_name) {
875                // DECY-127: Strip nested unsafe from value_code to avoid warnings
876                let clean_value = strip_unsafe(&value_code);
877                // DECY-143: Add SAFETY comment
878                return Self::unsafe_stmt(
879                    &format!("*{} = {}", target_code, clean_value),
880                    "pointer is valid, aligned, and not aliased during write",
881                );
882            }
883        }
884
885        // DECY-128: Check if target is Dereference(Variable) where variable holds a raw pointer
886        // e.g., **ptr = val where ptr is &mut *mut T
887        // *ptr yields *mut T (raw pointer), so **ptr needs unsafe
888        if let HirExpression::Dereference(inner) = target {
889            if let HirExpression::Variable(var_name) = &**inner {
890                // Check if dereferencing yields a raw pointer
891                // This happens when var_type is Reference to Pointer or Pointer to Pointer
892                if let Some(var_type) = ctx.get_type(var_name) {
893                    let yields_raw_ptr = match var_type {
894                        HirType::Reference { inner: ref_inner, .. } => {
895                            matches!(&**ref_inner, HirType::Pointer(_))
896                        }
897                        HirType::Pointer(ptr_inner) => {
898                            matches!(&**ptr_inner, HirType::Pointer(_))
899                        }
900                        _ => false,
901                    };
902                    if yields_raw_ptr {
903                        let clean_value = strip_unsafe(&value_code);
904                        // DECY-143: Add SAFETY comment
905                        return Self::unsafe_stmt(
906                            &format!("*{} = {}", target_code, clean_value),
907                            "double pointer dereference - inner pointer is valid and writable",
908                        );
909                    }
910                }
911            }
912        }
913
914        format!("*{} = {};", target_code, value_code)
915    }
916
917    /// Generate an array index assignment statement.
918    #[allow(clippy::borrowed_box)]
919    fn generate_array_index_assignment_statement(
920        &self,
921        array: &Box<HirExpression>,
922        index: &Box<HirExpression>,
923        value: &HirExpression,
924        ctx: &mut TypeContext,
925    ) -> String {
926        // Infer the type of array[index] for null pointer detection
927        let target_expr =
928            HirExpression::ArrayIndex { array: array.clone(), index: index.clone() };
929        let target_type = ctx.infer_expression_type(&target_expr);
930
931        // DECY-165: Check if array is a raw pointer - if so, use unsafe pointer arithmetic
932        let is_raw_pointer = if let HirExpression::Variable(var_name) = &**array {
933            ctx.is_pointer(var_name)
934        } else {
935            // Use type inference for complex expressions like sb->data
936            matches!(ctx.infer_expression_type(array), Some(HirType::Pointer(_)))
937        };
938
939        // DECY-223: Check for global array BEFORE generating code
940        let is_global_array = if let HirExpression::Variable(var_name) = &**array {
941            ctx.is_global(var_name)
942        } else {
943            false
944        };
945
946        // Generate array code - get raw name for globals to avoid double unsafe
947        let array_code = if is_global_array {
948            if let HirExpression::Variable(var_name) = &**array {
949                var_name.clone()
950            } else {
951                self.generate_expression_with_context(array, ctx)
952            }
953        } else {
954            self.generate_expression_with_context(array, ctx)
955        };
956        let index_code = self.generate_expression_with_context(index, ctx);
957        let mut value_code =
958            self.generate_expression_with_target_type(value, ctx, target_type.as_ref());
959
960        // DECY-210: Handle int-to-char coercion for array element assignment
961        // In C, s[i] = (n % 10) + '0' works because char is widened to int then truncated back
962        // In Rust, we need explicit cast when assigning int to u8 element
963        if matches!(target_type, Some(HirType::Char)) {
964            let value_type = ctx.infer_expression_type(value);
965            if matches!(value_type, Some(HirType::Int)) {
966                value_code = format!("({}) as u8", value_code);
967            }
968        }
969
970        if is_raw_pointer {
971            // Raw pointer indexing: arr[i] = v becomes unsafe { *arr.add(i as usize) = v }
972            // DECY-143: Add SAFETY comment
973            Self::unsafe_stmt(
974                &format!("*{}.add(({}) as usize) = {}", array_code, index_code, value_code),
975                "index is within bounds of allocated array",
976            )
977        } else {
978            // DECY-072: Cast index to usize for slice indexing
979            // DECY-150: Wrap index in parens to handle operator precedence
980            // DECY-223: Wrap global array assignment in unsafe block
981            if is_global_array {
982                format!(
983                    "unsafe {{ {}[({}) as usize] = {}; }}",
984                    array_code, index_code, value_code
985                )
986            } else {
987                format!("{}[({}) as usize] = {};", array_code, index_code, value_code)
988            }
989        }
990    }
991
992    /// Generate a field assignment statement.
993    fn generate_field_assignment_statement(
994        &self,
995        object: &HirExpression,
996        field: &str,
997        value: &HirExpression,
998        ctx: &mut TypeContext,
999    ) -> String {
1000        // DECY-227: Escape reserved keywords in field names
1001        let escaped_field = escape_rust_keyword(field);
1002        // Look up field type for null pointer detection
1003        let field_type = ctx.get_field_type(object, field);
1004        let obj_code = self.generate_expression_with_context(object, ctx);
1005        let value_code =
1006            self.generate_expression_with_target_type(value, ctx, field_type.as_ref());
1007
1008        // DECY-119: Check if object is a raw pointer - need unsafe deref
1009        let obj_type = if let HirExpression::Variable(name) = object {
1010            ctx.get_type(name)
1011        } else {
1012            None
1013        };
1014
1015        if matches!(obj_type, Some(HirType::Pointer(_))) {
1016            // Raw pointer field assignment needs unsafe block
1017            // DECY-143: Add SAFETY comment
1018            Self::unsafe_stmt(
1019                &format!("(*{}).{} = {}", obj_code, escaped_field, value_code),
1020                "pointer is non-null and points to valid struct with exclusive access",
1021            )
1022        } else {
1023            // DECY-261: Check if object is a global struct - use single unsafe block
1024            if let HirExpression::Variable(name) = object {
1025                if ctx.is_global(name) {
1026                    // Strip nested unsafe from value_code
1027                    fn strip_nested_unsafe(code: &str) -> String {
1028                        code.replace("unsafe { ", "").replacen(
1029                            " }",
1030                            "",
1031                            code.matches("unsafe { ").count(),
1032                        )
1033                    }
1034                    let clean_value = strip_nested_unsafe(&value_code);
1035                    return format!(
1036                        "unsafe {{ {}.{} = {}; }}",
1037                        name, escaped_field, clean_value
1038                    );
1039                }
1040            }
1041            // Regular struct field assignment
1042            format!("{}.{} = {};", obj_code, escaped_field, value_code)
1043        }
1044    }
1045}