fold/tests/integration/test_phi_merge.cpp

54 lines
2.1 KiB
C++

#include <clang/Frontend/ASTUnit.h>
#include <clang/Tooling/Tooling.h>
#include <catch2/catch_test_macros.hpp>
#include <memory>
#include <string>
#include "fold/detect/dead_branch.hpp"
#include "fold/report/finding.hpp"
#include "fold/symbolic/budget.hpp"
namespace {
std::unique_ptr<clang::ASTUnit> parse(const std::string& code) {
return clang::tooling::buildASTFromCodeWithArgs(code, {"-std=c++20"}, "input.cpp");
}
std::vector<fold::Finding> runDead(const std::string& code) {
auto unit = parse(code);
REQUIRE(unit);
fold::SolverBudget b;
fold::SolverStats s;
return fold::detectDeadBranchesInContext(unit->getASTContext(), b, s);
}
} // namespace
TEST_CASE("phi-merge: branch joining produces an unreachable downstream then", "[phi][dead]") {
// Both arms assign x to a small positive constant. After phi-merge the
// post-if value is ite(cond, 10, 20), which is provably never < 0. The
// second `if (x < 0)` then-arm becomes unreachable only if the walker
// merges the two arm states; without phi-merge the post-if x reverts to
// the pre-if free symbolic value and x < 0 is trivially feasible.
auto findings = runDead("int f(int arg){"
" int x;"
" if (arg > 0) { x = 10; } else { x = 20; }"
" if (x < 0) { return 1; }"
" return 0;"
"}");
REQUIRE(findings.size() == 1);
REQUIRE(findings[0].kind == fold::FindingKind::Dead);
REQUIRE(findings[0].confidence == fold::Confidence::Proved);
}
TEST_CASE("phi-merge: variable touched in one arm only inherits pre-if value", "[phi][dead]") {
auto findings = runDead("int f(int arg){"
" int x = 5;"
" if (arg > 0) { x = 10; }"
" if (x < 0) { return -1; }"
" return x;"
"}");
// Both possible values of x (5 or 10) are non-negative, so x < 0 is dead.
REQUIRE(findings.size() == 1);
}