// Measurement-only first cut for matrix row C5 // (`clang::UninitializedValues` pre-pass). The original framing — // "triage null-deref FPs via uninit pre-pass" — was a wrong-hypothesis: // clang's analyzer flags LOCAL read-before-write, not parameter // nullability. This test pins what the analyzer *does* report so the // fold-side wrapper stays honest about its scope. Future sessions can // decide whether to graduate the signal into a standalone detector. #include #include #include #include #include #include "fold/symbolic/uninit_pre_pass.hpp" namespace { std::unique_ptr parse(const std::string& code) { return clang::tooling::buildASTFromCodeWithArgs(code, {"-std=c++20"}, "input.cpp"); } } // namespace TEST_CASE("uninit pre-pass: function with no uninit reads scores 0", "[uninit-pre-pass][clean]") { auto unit = parse(R"( int safe(int x) { int y = x + 1; return y; } )"); REQUIRE(unit); auto stats = fold::symbolic::runUninitAnalysisOnTU(unit->getASTContext()); REQUIRE(stats.total_functions >= 1); REQUIRE(stats.functions_with_uninit_reads == 0); REQUIRE(stats.total_uninit_reads == 0); } TEST_CASE("uninit pre-pass: read-before-write local is flagged", "[uninit-pre-pass][always-uninit]") { auto unit = parse(R"( int oops() { int y; return y + 1; } )"); REQUIRE(unit); auto stats = fold::symbolic::runUninitAnalysisOnTU(unit->getASTContext()); REQUIRE(stats.total_functions >= 1); REQUIRE(stats.functions_with_uninit_reads >= 1); REQUIRE(stats.total_uninit_reads >= 1); } TEST_CASE("uninit pre-pass: branch-conditional uninit is flagged", "[uninit-pre-pass][sometimes-uninit]") { auto unit = parse(R"( int maybe(int cond) { int y; if (cond) y = 7; return y; } )"); REQUIRE(unit); auto stats = fold::symbolic::runUninitAnalysisOnTU(unit->getASTContext()); REQUIRE(stats.functions_with_uninit_reads >= 1); } TEST_CASE("uninit pre-pass: parameters are never flagged uninit (scope check)", "[uninit-pre-pass][scope]") { // The original C5 hypothesis was "null-deref FP triage via uninit // pre-pass." This test pins the reason that hypothesis was wrong: // clang's analyzer treats function parameters as always-initialized. // A param can still be nullable, but the uninit analysis won't say so. auto unit = parse(R"( int param_only(int* p) { return *p; } )"); REQUIRE(unit); auto stats = fold::symbolic::runUninitAnalysisOnTU(unit->getASTContext()); REQUIRE(stats.functions_with_uninit_reads == 0); }