fold/tests/unit/test_int_truncation.cpp
trvon fd4a68dfff
test: add coverage for project graph, pcode encoder, and detector behaviors
Unit + integration coverage for the session's work: incremental project
graph/index, pcode encoder, unreachable/dead-branch, equiv/cross-TU,
div-by-zero/sign-conversion/int-truncation, use-after-free, quality-precision,
scan-router profile, source TU runner, detector plan, and the eval fixtures.
2026-08-16 16:46:07 -06:00

65 lines
2.4 KiB
C++

#include <clang/Frontend/ASTUnit.h>
#include <clang/Tooling/Tooling.h>
#include <catch2/catch_test_macros.hpp>
#include <memory>
#include <string>
#include <vector>
#include "fold/detect/int_truncation.hpp"
#include "fold/report/finding.hpp"
#include "fold/symbolic/budget.hpp"
namespace {
struct TestResult {
std::vector<fold::Finding> findings;
fold::SolverStats stats;
};
TestResult run(const std::string& code) {
auto unit = clang::tooling::buildASTFromCodeWithArgs(code, {"-std=c++20"}, "input.cpp");
REQUIRE(unit);
fold::SolverBudget b;
fold::SolverStats s;
auto findings = fold::detectIntTruncationInContext(unit->getASTContext(), b, s);
return {std::move(findings), s};
}
} // namespace
TEST_CASE("int-truncation: `char c = 256` in entry-less extern is latent",
"[truncation][literal]") {
auto [findings, stats] = run("int f(){ char c = 256; return c; }");
REQUIRE(findings.size() == 1);
REQUIRE(findings[0].kind == fold::FindingKind::IntTruncation);
// `f` is an extern function in an entry-less TU (no bad()/good()/main):
// the finding is caller-precondition at the file level (iter-291
// function-level helper gate), so it is demoted to latent. Gold labels
// only `_NN a` entry files, where this stays Proved via tuHasJulietEntry.
REQUIRE(findings[0].evidence.latent);
REQUIRE(findings[0].confidence == fold::Confidence::Latent);
REQUIRE(stats.queries == 1);
REQUIRE(stats.sat == 1);
}
TEST_CASE("int-truncation: bounded narrowing is suppressed", "[truncation][guarded]") {
auto [findings, stats] =
run("int f(int x){ if (x >= 0 && x < 100) { short s = x; return s; } return 0; }");
REQUIRE(findings.empty());
REQUIRE(stats.queries == 1);
REQUIRE(stats.unsat == 1);
}
TEST_CASE("int-truncation: havoc-backed SAT result is latent (entry-less extern)",
"[truncation][havoc][precision]") {
auto [findings, stats] =
run("extern void mutate(int&); int f(){ int x = 0; mutate(x); short y = x; return y; }");
REQUIRE(findings.size() == 1);
// `f` is an extern function in an entry-less TU (no bad()/good()/main):
// the truncation of a caller-mutated value is caller-precondition, so the
// finding is demoted to latent (iter-291 function-level helper gate).
REQUIRE(findings[0].evidence.latent);
REQUIRE(findings[0].confidence == fold::Confidence::Latent);
REQUIRE(stats.sat == 1);
}