From 15c6118ef92a223b7fa1edd1dc7a3c5728453d9c Mon Sep 17 00:00:00 2001 From: Bappoz Date: Wed, 24 Jun 2026 01:59:51 -0300 Subject: [PATCH 1/2] =?UTF-8?q?test(e2e):=20cobrir=20blocos,=20if/else,=20?= =?UTF-8?q?while,=20for,=20do-while=20e=20recurs=C3=A3o=20em=20smoke=20tes?= =?UTF-8?q?ts=20reais?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Estende tests/exe_smoke_test.rs com casos executados de ponta a ponta (fonte C -> lexer -> parser -> semantica -> IR -> codegen x86-64 -> gcc) para blocos/escopos locais, cadeia if/else if/else, while, for, do-while e recursao (fibonacci), complementando os smoke tests ja existentes de programa minimo, aritmetica e chamada de funcao. Resolve #161 --- tests/exe_smoke_test.rs | 106 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/tests/exe_smoke_test.rs b/tests/exe_smoke_test.rs index 63d4f45..c36cd7b 100644 --- a/tests/exe_smoke_test.rs +++ b/tests/exe_smoke_test.rs @@ -141,3 +141,109 @@ fn smoke_function_call_runs() { #[cfg(unix)] assert_eq!(status.code(), Some(42)); } + +#[test] +fn smoke_blocks_and_local_scopes_run() { + require_gcc!(); + + let status = compile_and_run( + "blocks", + "int main() { \ + int total = 0; \ + { int a = 5; total = total + a; } \ + { int b = 7; total = total + b; } \ + return total; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(12)); +} + +#[test] +fn smoke_if_else_chain_runs() { + require_gcc!(); + + let status = compile_and_run( + "if_else_chain", + "int classify(int n) { \ + if (n < 0) { return -1; } \ + else if (n == 0) { return 0; } \ + else { return 1; } \ + } \ + int main() { return classify(-5) + classify(0) + classify(5) + 10; }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(10)); +} + +#[test] +fn smoke_while_loop_runs() { + require_gcc!(); + + let status = compile_and_run( + "while", + "int main() { \ + int i = 0; \ + int total = 0; \ + while (i < 5) { total = total + i; i = i + 1; } \ + return total; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(10)); +} + +#[test] +fn smoke_for_loop_runs() { + require_gcc!(); + + let status = compile_and_run( + "for", + "int main() { \ + int total = 0; \ + for (int i = 0; i < 5; i = i + 1) { total = total + i; } \ + return total; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(10)); +} + +#[test] +fn smoke_do_while_loop_runs() { + require_gcc!(); + + let status = compile_and_run( + "do_while", + "int main() { \ + int i = 0; \ + int total = 0; \ + do { total = total + i; i = i + 1; } while (i < 5); \ + return total; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(10)); +} + +#[test] +fn smoke_recursive_fibonacci_runs() { + require_gcc!(); + + let status = compile_and_run( + "fib", + "int fib(int n) { \ + if (n <= 1) { return n; } \ + return fib(n - 1) + fib(n - 2); \ + } \ + int main() { return fib(10); }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(55)); +} From e1b7577239575d6c370c3c8d7f3766f30c968235 Mon Sep 17 00:00:00 2001 From: Hugo Freitas Silva Date: Wed, 24 Jun 2026 18:21:15 -0300 Subject: [PATCH 2/2] fix(tests): restore smoke coverage after merge --- src/codegen/last/x86_64.rs | 5 +- src/ir/cfg.rs | 6 +- tests/codegen_smoke.rs | 2 + tests/exe_smoke_test.rs | 224 +++++++++++++++++++++++++++++++++++++ 4 files changed, 231 insertions(+), 6 deletions(-) diff --git a/src/codegen/last/x86_64.rs b/src/codegen/last/x86_64.rs index 8cb90cd..497791c 100644 --- a/src/codegen/last/x86_64.rs +++ b/src/codegen/last/x86_64.rs @@ -152,9 +152,10 @@ pub fn emit_program(prog: &TacProgram) -> EmitResult { em.blank(); em.append_str(&emit_function(func, &strings)?); } - // Marca a stack como nao-executavel (boa pratica; evita aviso do linker e - // e o que o proprio GCC adiciona a saida assembly). + // Marca a stack como nao-executavel em formatos ELF. Essa secao nao + // existe no COFF usado pelo MinGW e tornaria o assembly invalido la. em.blank(); + #[cfg(not(target_os = "windows"))] em.raw(".section .note.GNU-stack,\"\",@progbits"); Ok(em.into_string()) } diff --git a/src/ir/cfg.rs b/src/ir/cfg.rs index 6eff37d..34a236e 100644 --- a/src/ir/cfg.rs +++ b/src/ir/cfg.rs @@ -68,10 +68,8 @@ pub fn identify_leaders(instrs: &[TacInstr]) -> HashSet { for (index, instr) in instrs.iter().enumerate() { match instr { - TacInstr::Jump { .. } | TacInstr::CondJump { .. } => { - if index + 1 < instrs.len() { - leaders.insert(index + 1); - } + TacInstr::Jump { .. } | TacInstr::CondJump { .. } if index + 1 < instrs.len() => { + leaders.insert(index + 1); } TacInstr::Label(_) => { leaders.insert(index); diff --git a/tests/codegen_smoke.rs b/tests/codegen_smoke.rs index fc801b9..a545663 100644 --- a/tests/codegen_smoke.rs +++ b/tests/codegen_smoke.rs @@ -6,6 +6,8 @@ //! execucao. Se o `gcc` nao estiver disponivel no ambiente, os testes sao //! ignorados (skip) em vez de falhar. +#![cfg_attr(not(unix), allow(unused_variables))] + use std::path::PathBuf; use std::process::Command; diff --git a/tests/exe_smoke_test.rs b/tests/exe_smoke_test.rs index e1f959b..2a190d2 100644 --- a/tests/exe_smoke_test.rs +++ b/tests/exe_smoke_test.rs @@ -8,6 +8,8 @@ //! completo. Se `gcc` nao estiver disponivel no ambiente, os testes sao //! ignorados (skip) em vez de falhar. +#![cfg_attr(not(unix), allow(unused_variables))] + use std::path::PathBuf; use std::process::{Command, ExitStatus}; @@ -248,3 +250,225 @@ fn smoke_recursive_fibonacci_runs() { #[cfg(unix)] assert_eq!(status.code(), Some(55)); } + +#[test] +fn smoke_switch_with_default_runs() { + require_gcc!(); + + let status = compile_and_run( + "switch_default", + "int classify(int n) { \ + int result = 0; \ + switch (n) { \ + case 1: result = 1; break; \ + case 2: result = 2; break; \ + default: result = -1; break; \ + } \ + return result; \ + } \ + int main() { return classify(1) + classify(2) + classify(9) + 100; }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(102)); +} + +#[test] +fn smoke_address_of_and_deref_read_runs() { + require_gcc!(); + + let status = compile_and_run( + "addrof_deref_read", + "int main() { \ + int x = 21; \ + int *p = &x; \ + int y = *p; \ + return y * 2; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(42)); +} + +#[test] +fn smoke_deref_assignment_writes_through_pointer() { + require_gcc!(); + + let status = compile_and_run( + "deref_assign", + "int main() { \ + int x = 10; \ + int *p = &x; \ + *p = 20; \ + return x; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(20)); +} + +#[test] +fn smoke_deref_compound_assign_through_function_param_runs() { + require_gcc!(); + + let status = compile_and_run( + "deref_compound", + "void inc(int *p) { *p = *p + 1; } \ + int main() { \ + int x = 10; \ + inc(&x); \ + inc(&x); \ + return x; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(12)); +} + +#[test] +fn smoke_deref_increment_operators_run() { + require_gcc!(); + + let status = compile_and_run( + "deref_incr", + "int main() { \ + int x = 5; \ + int *p = &x; \ + (*p)++; \ + *p += 10; \ + return x; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(16)); +} + +#[test] +fn smoke_pointer_index_read_and_write_runs() { + require_gcc!(); + + let status = compile_and_run( + "pointer_index", + "int sum_via_index(int *p) { \ + p[0] = 10; \ + return p[0] + 5; \ + } \ + int main() { \ + int x = 1; \ + return sum_via_index(&x); \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(15)); +} + +#[test] +fn smoke_struct_member_read_and_write_runs() { + require_gcc!(); + + let status = compile_and_run( + "struct_member", + "struct Point { int x; int y; }; \ + int main() { \ + struct Point p; \ + p.x = 3; \ + p.y = 4; \ + return p.x + p.y; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(7)); +} + +#[test] +fn smoke_struct_member_via_pointer_arrow_runs() { + require_gcc!(); + + let status = compile_and_run( + "struct_member_arrow", + "struct Point { int x; int y; }; \ + void move_point(struct Point *p, int dx, int dy) { \ + p->x = p->x + dx; \ + p->y = p->y + dy; \ + } \ + int main() { \ + struct Point p; \ + p.x = 1; \ + p.y = 2; \ + move_point(&p, 10, 20); \ + return p.x + p.y; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(33)); +} + +#[test] +fn smoke_struct_larger_than_eight_bytes_runs() { + require_gcc!(); + + let status = compile_and_run( + "struct_big", + "struct Big { long a; long b; long c; }; \ + int main() { \ + struct Big big; \ + big.a = 1; \ + big.b = 2; \ + big.c = 3; \ + return big.a + big.b + big.c; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(6)); +} + +#[test] +fn smoke_sizeof_of_variables_runs() { + require_gcc!(); + + let status = compile_and_run( + "sizeof_vars", + "int main() { \ + int x = 7; \ + long l = 3; \ + char c = 'a'; \ + int *p = &x; \ + return sizeof(x) + sizeof(l) + sizeof(c) + sizeof(p); \ + }", + ); + + // sizeof(int) + sizeof(long) + sizeof(char) + sizeof(int*) = 4+8+1+8. + #[cfg(unix)] + assert_eq!(status.code(), Some(21)); +} + +#[test] +fn smoke_switch_fallthrough_runs() { + require_gcc!(); + + let status = compile_and_run( + "switch_fallthrough", + "int main() { \ + int n = 2; \ + int total = 0; \ + switch (n) { \ + case 1: total = total + 1; \ + case 2: total = total + 2; \ + case 3: total = total + 3; break; \ + case 4: total = total + 100; \ + } \ + return total; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(5)); +}