// Matrix row C5-wiring: graduate the UninitializedValues pre-pass // from a measurement scaffold into a real detector that emits // `uninit-local-read` findings. C5's library function was already // shipped (see test_uninit_pre_pass.cpp); this test pins the // scan-pipeline-facing entry point. #include #include #include #include #include #include "fold/detect/uninit_local_read.hpp" #include "fold/report/finding.hpp" namespace { std::unique_ptr parse(const std::string& code) { return clang::tooling::buildASTFromCodeWithArgs(code, {"-std=c++20"}, "input.cpp"); } } // namespace TEST_CASE("uninit-local-read: int x; int y = x; emits a finding", "[uninit-local-read][positive]") { auto unit = parse(R"( int leak() { int x; int y = x; return y; } )"); REQUIRE(unit); auto findings = fold::detectUninitLocalReadInContext(unit->getASTContext()); REQUIRE(findings.size() >= 1); REQUIRE(findings[0].kind == fold::FindingKind::UninitLocalRead); } TEST_CASE("uninit-local-read: int y = x where x is initialised does not fire", "[uninit-local-read][negative]") { auto unit = parse(R"( int safe(int x) { int y = x; return y; } )"); REQUIRE(unit); auto findings = fold::detectUninitLocalReadInContext(unit->getASTContext()); REQUIRE(findings.empty()); } TEST_CASE("uninit-local-read: parameter reads are NOT flagged", "[uninit-local-read][param-scope]") { // C5's calibrated scope: clang's analyzer flags only LOCAL // read-before-write. Parameters are always treated as initialised // (they're not — but encoding nullability requires a different // analyzer). Pin the scope. auto unit = parse(R"( int reads_param(int p) { return p; } )"); REQUIRE(unit); auto findings = fold::detectUninitLocalReadInContext(unit->getASTContext()); REQUIRE(findings.empty()); } TEST_CASE("uninit-local-read: finding location points at the read, not the decl", "[uninit-local-read][location]") { auto unit = parse(R"( int leak() { int x; return x; } )"); REQUIRE(unit); auto findings = fold::detectUninitLocalReadInContext(unit->getASTContext()); REQUIRE(findings.size() >= 1); REQUIRE_FALSE(findings[0].locations.empty()); // The `return x;` line is 4 in the parsed snippet (line 1 is the // opening blank-after-R"(); int leak() is line 2; int x; is line // 3; return x; is line 4). The decl-only location would be line 3. REQUIRE(findings[0].locations[0].line == 4); } TEST_CASE("uninit-local-read: self-init `int x = x` is flagged", "[uninit-local-read][self-init]") { auto unit = parse(R"( int self_init() { int x = x; return x; } )"); REQUIRE(unit); auto findings = fold::detectUninitLocalReadInContext(unit->getASTContext()); REQUIRE(findings.size() >= 1); } TEST_CASE("uninit-local-read: strtol endptr write counts as initialisation", "[uninit-local-read][strtol]") { auto unit = parse(R"( extern "C" long strtol(const char*, char**, int); int parse_tail() { char input[] = "7"; char* end; (void)strtol(input, &end, 10); return *end == '\0'; } )"); REQUIRE(unit); auto findings = fold::detectUninitLocalReadInContext(unit->getASTContext()); REQUIRE(findings.empty()); } TEST_CASE("uninit-local-read: statement expression result read is not flagged", "[uninit-local-read][stmt-expr]") { // ARM NEON intrinsics (vaddvq_*/vminvq_*/vmaxvq_*) expand to GNU statement // expressions whose result reads an internal `__ret` written by a preceding // assignment inside the same expression. A flat traversal would flag the // result read; the walker must honor the intra-expression write ordering. auto unit = parse(R"( extern "C" float g(void*); float f(void* acc) { float sum = ({ void* __a = acc; float __ret; __ret = g(__a); __ret; }); return sum; } )"); REQUIRE(unit); auto findings = fold::detectUninitLocalReadInContext(unit->getASTContext()); REQUIRE(findings.empty()); } TEST_CASE("uninit-local-read: genuinely uninitialized local still fires", "[uninit-local-read][stmt-expr][negative]") { auto unit = parse(R"( int f() { int x; return x; } )"); REQUIRE(unit); auto findings = fold::detectUninitLocalReadInContext(unit->getASTContext()); REQUIRE(findings.size() >= 1); REQUIRE(findings[0].kind == fold::FindingKind::UninitLocalRead); }