diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml index 8178015..3685433 100644 --- a/.github/workflows/valgrind.yml +++ b/.github/workflows/valgrind.yml @@ -20,10 +20,5 @@ jobs: run: make all - name: Run tests under Valgrind - run: | - valgrind --leak-check=full \ - --show-leak-kinds=all \ - --track-origins=yes \ - --error-exitcode=1 \ - ./build/cleaf test.clf + run: make valgrind-test diff --git a/Makefile b/Makefile index de0f208..49558cf 100644 --- a/Makefile +++ b/Makefile @@ -62,8 +62,33 @@ asan-test: CFLAGS="-fsanitize=address,undefined -g -O1" make test valgrind-test: - valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test.clf - + @echo "Testing memory safety with valgrind..." + @echo "=== Testing normal execution ===" + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/full.clf + @echo "=== Testing lexer errors ===" + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/lexer_error.clf + @echo "=== Testing parser errors ===" + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/parser_missing_semicolon.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/parser_unmatched_brace.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/parser_missing_paren.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/parser_incomplete_function.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/parser_missing_function_name.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/parser_missing_var_name.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/parser_for_missing_increment.clf + @echo "=== Testing semantic errors ===" + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/semantic_function_reserved.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/semantic_undefined_vars.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/semantic_type_mismatch.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/semantic_var_redefinition.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/semantic_undefined_function.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/semantic_function_duplicate.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/semantic_function_args_error.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/semantic_return_type_error.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/semantic_unary_type_error.clf + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/semantic_control_flow_errors.clf + @echo "=== Testing combined errors ===" + valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./build/cleaf test/valgrind_case/combined_multiple_errors.clf + @echo "=== All valgrind tests passed ===" clean: rm -rf $(BUILD) diff --git a/src/frontend/ast.c b/src/frontend/ast.c index ad31792..ff708d7 100644 --- a/src/frontend/ast.c +++ b/src/frontend/ast.c @@ -1416,7 +1416,8 @@ statement_t* parse_statement(parser_t* p) return ast_parse_decl_stmt(p); } - // for now, we can maybe assume that this is the always wanted fallback - // TODO: keep an eye on this - return ast_parse_expr_stmt(p); + if (p->pos < p->count) + return ast_parse_expr_stmt(p); + + return NULL; } diff --git a/test/valgrind_case/README.md b/test/valgrind_case/README.md new file mode 100644 index 0000000..c9800c6 --- /dev/null +++ b/test/valgrind_case/README.md @@ -0,0 +1,8 @@ +# Valgrind test case + +The goal of this dir is to create cleaf program that valgrind will test to verify that there is no memory leaks. + +Here is the strategy: + +- maintain some base cleaf program that runs completely to check memory safety in a normal environment +- test every early compiler termination by adding cleaf files that should not compile to check that not running the compiler entirely don't cause memory leak diff --git a/test/valgrind_case/combined_multiple_errors.clf b/test/valgrind_case/combined_multiple_errors.clf new file mode 100644 index 0000000..719b667 --- /dev/null +++ b/test/valgrind_case/combined_multiple_errors.clf @@ -0,0 +1,25 @@ +fn test(int a, int a): string { + var b = undefined_var + 5; + int c = "string" + 10; + var c = 20; + return 42; +} + +fn test(): int { + return "duplicate function"; +} + +fn main() { + int x = missing_func(1, 2); + var y = test(5 + string z = x + "text"; + + for (var i = 0; undef_var < 10; ++i) { + int i = 0; + var result = i + "string"; + } + + if (another_missing { + x = y; + +} diff --git a/test/valgrind_case/full.clf b/test/valgrind_case/full.clf new file mode 100644 index 0000000..f8889b0 --- /dev/null +++ b/test/valgrind_case/full.clf @@ -0,0 +1,36 @@ +fn main(): int { + var a = bar(0); + int b = bar(0); + + string c = foo(); + + var d = a + b; + + for (var i = 0; i < a + 10; ++i) { + if (b == 0) { + d++; + } + } + + while (d > 0) { + d = d - b + 1; + } + + if (d == 0) { + d = 4; + } else { + d = 0; + } + + return 0; +} + + +fn foo(): string { + var b = "test"; + return b; +} + +fn bar(int a): int { + return a; +} diff --git a/test/valgrind_case/lexer_error.clf b/test/valgrind_case/lexer_error.clf new file mode 100644 index 0000000..184791d --- /dev/null +++ b/test/valgrind_case/lexer_error.clf @@ -0,0 +1,3 @@ +fn main() { + var a = @invalid; +} diff --git a/test/valgrind_case/parser_for_missing_increment.clf b/test/valgrind_case/parser_for_missing_increment.clf new file mode 100644 index 0000000..4b771aa --- /dev/null +++ b/test/valgrind_case/parser_for_missing_increment.clf @@ -0,0 +1,5 @@ +fn main() { + for (var i = 0; i < 10) { + i++; + } +} diff --git a/test/valgrind_case/parser_incomplete_function.clf b/test/valgrind_case/parser_incomplete_function.clf new file mode 100644 index 0000000..c12a976 --- /dev/null +++ b/test/valgrind_case/parser_incomplete_function.clf @@ -0,0 +1 @@ +fn main( diff --git a/test/valgrind_case/parser_missing_function_name.clf b/test/valgrind_case/parser_missing_function_name.clf new file mode 100644 index 0000000..75a916e --- /dev/null +++ b/test/valgrind_case/parser_missing_function_name.clf @@ -0,0 +1,3 @@ +fn { + return 0; +} diff --git a/test/valgrind_case/parser_missing_paren.clf b/test/valgrind_case/parser_missing_paren.clf new file mode 100644 index 0000000..4454218 --- /dev/null +++ b/test/valgrind_case/parser_missing_paren.clf @@ -0,0 +1,3 @@ +fn main() { + var a = (5 + 3; +} diff --git a/test/valgrind_case/parser_missing_semicolon.clf b/test/valgrind_case/parser_missing_semicolon.clf new file mode 100644 index 0000000..2ff69c5 --- /dev/null +++ b/test/valgrind_case/parser_missing_semicolon.clf @@ -0,0 +1,4 @@ +fn main() { + var a = 5 + var b = 10; +} diff --git a/test/valgrind_case/parser_missing_var_name.clf b/test/valgrind_case/parser_missing_var_name.clf new file mode 100644 index 0000000..d486f58 --- /dev/null +++ b/test/valgrind_case/parser_missing_var_name.clf @@ -0,0 +1,3 @@ +fn main() { + var = 5; +} diff --git a/test/valgrind_case/parser_unmatched_brace.clf b/test/valgrind_case/parser_unmatched_brace.clf new file mode 100644 index 0000000..c9015f5 --- /dev/null +++ b/test/valgrind_case/parser_unmatched_brace.clf @@ -0,0 +1,6 @@ +fn main() { + var a = 5; + if (a > 0) { + a = 10; + +} diff --git a/test/valgrind_case/semantic_control_flow_errors.clf b/test/valgrind_case/semantic_control_flow_errors.clf new file mode 100644 index 0000000..f874508 --- /dev/null +++ b/test/valgrind_case/semantic_control_flow_errors.clf @@ -0,0 +1,14 @@ +fn main() { + int a = 5; + for (var i = 0; undefined_cond < 10; ++i) { + a++; + } + + while (missing_var > 0) { + a--; + } + + if (another_undef) { + a = 0; + } +} diff --git a/test/valgrind_case/semantic_function_args_error.clf b/test/valgrind_case/semantic_function_args_error.clf new file mode 100644 index 0000000..e19c103 --- /dev/null +++ b/test/valgrind_case/semantic_function_args_error.clf @@ -0,0 +1,9 @@ +fn add(int a, int b): int { + return a + b; +} + +fn main() { + var x = add(5); + var y = add(1, 2, 3); + var z = add("test", 5); +} diff --git a/test/valgrind_case/semantic_function_duplicate.clf b/test/valgrind_case/semantic_function_duplicate.clf new file mode 100644 index 0000000..21fa962 --- /dev/null +++ b/test/valgrind_case/semantic_function_duplicate.clf @@ -0,0 +1,11 @@ +fn foo() { + return 0; +} + +fn foo() { + return 1; +} + +fn main() { + return 0; +} diff --git a/test/valgrind_case/semantic_function_reserved.clf b/test/valgrind_case/semantic_function_reserved.clf new file mode 100644 index 0000000..74b1563 --- /dev/null +++ b/test/valgrind_case/semantic_function_reserved.clf @@ -0,0 +1,3 @@ +fn var(): { + return 0; +} diff --git a/test/valgrind_case/semantic_return_type_error.clf b/test/valgrind_case/semantic_return_type_error.clf new file mode 100644 index 0000000..52de8ad --- /dev/null +++ b/test/valgrind_case/semantic_return_type_error.clf @@ -0,0 +1,7 @@ +fn test(): string { + return 42; +} + +fn main(): int { + return "not an int"; +} diff --git a/test/valgrind_case/semantic_type_mismatch.clf b/test/valgrind_case/semantic_type_mismatch.clf new file mode 100644 index 0000000..3248969 --- /dev/null +++ b/test/valgrind_case/semantic_type_mismatch.clf @@ -0,0 +1,6 @@ +fn main() { + int a = 5; + string b = "test"; + var c = a + b; + var d = b - 10; +} diff --git a/test/valgrind_case/semantic_unary_type_error.clf b/test/valgrind_case/semantic_unary_type_error.clf new file mode 100644 index 0000000..f39cd7f --- /dev/null +++ b/test/valgrind_case/semantic_unary_type_error.clf @@ -0,0 +1,6 @@ +fn main() { + var a = -"string"; + int b = !"test"; + string c = "hello"; + var d = ++c; +} diff --git a/test/valgrind_case/semantic_undefined_function.clf b/test/valgrind_case/semantic_undefined_function.clf new file mode 100644 index 0000000..e6f7c91 --- /dev/null +++ b/test/valgrind_case/semantic_undefined_function.clf @@ -0,0 +1,4 @@ +fn main() { + var result = undefined_function(); + another_missing(5, 10); +} diff --git a/test/valgrind_case/semantic_undefined_vars.clf b/test/valgrind_case/semantic_undefined_vars.clf new file mode 100644 index 0000000..070f8d3 --- /dev/null +++ b/test/valgrind_case/semantic_undefined_vars.clf @@ -0,0 +1,4 @@ +fn main() { + int a = undefined_var + 5; + var b = another_undef; +} diff --git a/test/valgrind_case/semantic_var_redefinition.clf b/test/valgrind_case/semantic_var_redefinition.clf new file mode 100644 index 0000000..5558358 --- /dev/null +++ b/test/valgrind_case/semantic_var_redefinition.clf @@ -0,0 +1,6 @@ +fn main() { + int a = 5; + int a = 10; + var b = 3; + string b = "test"; +}