From 63f0a6c0e7c03e09e3bc7724b017bf74517dd7c8 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Sat, 15 Aug 2026 17:22:59 +0200 Subject: [PATCH] test(parse-coverage): free the extraction results in the #1610 tests LeakSanitizer on CI caught all five new tests leaking their CBMFileResult: Indirect leak of 24 byte(s) ... ts_tree_new cbm_extract_file_ex cbm.c:1256 do_extract test_parse_coverage.c:39 test_dockerfile_missing_final_newline_not_flagged_issue1610:272 SUMMARY: AddressSanitizer: 706504 byte(s) leaked in 189 allocation(s) Every pre-existing test in this suite calls cbm_free_result before PASS; the new ones did not. The local run could not have found it - LeakSanitizer reports "detect_leaks is not supported on this platform" on macOS arm64, so this class of defect is CI-only here. Each test now captures what it asserts, frees, and only then decides, so the early-FAIL paths do not leak either. The cross-grammar loop prints its diagnostic before freeing so the failure message keeps naming the grammar. While correcting the guard, a first attempt left ASSERT_TRUE(flagged || has_ranges || true) in real_error_before_eof_still_flagged - always true, and it would have silently disarmed the guard that stops the EOF suppression from being over-broad. Removed. The guard is re-proven binding: forcing cbm_is_eof_terminator_miss to return true makes EIGHT tests fail, including both guards, and restoring it returns the suite to green. parse_coverage 14 passed. Signed-off-by: Martin Vogel --- tests/test_parse_coverage.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/test_parse_coverage.c b/tests/test_parse_coverage.c index 39ce43c20..1c0720c94 100644 --- a/tests/test_parse_coverage.c +++ b/tests/test_parse_coverage.c @@ -272,11 +272,10 @@ TEST(dockerfile_missing_final_newline_not_flagged_issue1610) { CBMFileResult *r = do_extract(src, CBM_LANG_DOCKERFILE, "Dockerfile"); ASSERT_NOT_NULL(r); bool flagged = r->parse_incomplete; - const char *ranges = r->error_ranges; + cbm_free_result(r); if (flagged) { FAIL("a Dockerfile lacking only its final newline must not be parse_partial"); } - (void)ranges; PASS(); } @@ -288,7 +287,9 @@ TEST(dockerfile_with_final_newline_still_clean_issue1610) { "ENTRYPOINT [\"dotnet\", \"App.dll\"]\n"; CBMFileResult *r = do_extract(src, CBM_LANG_DOCKERFILE, "Dockerfile"); ASSERT_NOT_NULL(r); - if (r->parse_incomplete) { + bool flagged = r->parse_incomplete; + cbm_free_result(r); + if (flagged) { FAIL("a terminated Dockerfile must not be parse_partial"); } PASS(); @@ -310,9 +311,13 @@ TEST(missing_final_newline_not_flagged_across_grammars_issue1610) { for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { CBMFileResult *r = do_extract(cases[i].src, cases[i].lang, cases[i].path); ASSERT_NOT_NULL(r); - if (r->parse_incomplete) { + bool flagged = r->parse_incomplete; + if (flagged) { fprintf(stderr, " %s flagged: ranges=%s\n", cases[i].path, r->error_ranges ? r->error_ranges : "(none)"); + } + cbm_free_result(r); + if (flagged) { FAIL("an unterminated final line must not be parse_partial in any grammar"); } } @@ -335,11 +340,11 @@ TEST(real_error_before_eof_still_flagged_without_final_newline_issue1610) { unterminated[n - 1] = '\0'; /* drop the final newline */ CBMFileResult *r = do_extract(unterminated, CBM_LANG_C, "split.c"); - bool flagged = r && r->parse_incomplete; - bool has_ranges = r && r->error_ranges != NULL; free(unterminated); - ASSERT_NOT_NULL(r); + bool flagged = r->parse_incomplete; + bool has_ranges = r->error_ranges != NULL; + cbm_free_result(r); if (!flagged) { FAIL("a real mid-file parse failure must still be reported when the file also lacks its final newline"); } @@ -356,7 +361,9 @@ TEST(width_bearing_error_at_eof_still_flagged_issue1610) { const char *src = "all:\n\techo hi"; /* no trailing newline; recipe is lost */ CBMFileResult *r = do_extract(src, CBM_LANG_MAKEFILE, "Makefile"); ASSERT_NOT_NULL(r); - if (!r->parse_incomplete) { + bool flagged = r->parse_incomplete; + cbm_free_result(r); + if (!flagged) { FAIL("a width-bearing parse failure at EOF must still be reported"); } PASS();