- persist TU-owned graphs and guarded configuration evidence - add closed-world production reachability classification - calibrate detector precision and reviewed self-scan policy - terminate poisoned workers instead of longjmp recovery
102 lines
4.1 KiB
C++
102 lines
4.1 KiB
C++
#include <clang/Tooling/Tooling.h>
|
|
|
|
#include <algorithm>
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "fold/analysis/production_roots.hpp"
|
|
#include "fold/config/configuration_profile.hpp"
|
|
|
|
namespace {
|
|
|
|
using fold::analysis::ProductionRootKind;
|
|
|
|
bool hasRoot(const std::vector<fold::analysis::ProductionRootRecord>& roots,
|
|
const std::string& name, ProductionRootKind kind) {
|
|
return std::ranges::any_of(
|
|
roots, [&](const auto& root) { return root.qualified_name == name && root.kind == kind; });
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("production root discovery records explicit conventional and escape roots",
|
|
"[production-roots]") {
|
|
constexpr const char* source = R"cpp(
|
|
extern "C" void exported_api() {}
|
|
void register_callback(void (*)()) {}
|
|
void callback() {}
|
|
void escaped() {}
|
|
void direct_only() {}
|
|
void static_entry() {}
|
|
int global_value = (static_entry(), 1);
|
|
struct Concrete { virtual void run() {} };
|
|
void manual() {}
|
|
int main() {
|
|
register_callback(callback);
|
|
auto pointer = &escaped;
|
|
(void)pointer;
|
|
direct_only();
|
|
Concrete concrete;
|
|
concrete.run();
|
|
return 0;
|
|
}
|
|
)cpp";
|
|
auto ast =
|
|
clang::tooling::buildASTFromCodeWithArgs(source, {"-std=c++20"}, "/project/src/root.cpp");
|
|
REQUIRE(ast);
|
|
const auto profile = fold::config::parseConfigurationProfileText(
|
|
R"({"version":1,"entrypoints":["manual"],"assumptions":[]})");
|
|
REQUIRE(profile);
|
|
|
|
std::vector<fold::analysis::ProductionRootRecord> roots;
|
|
fold::analysis::collectProductionRootRecords(ast->getASTContext(), &*profile.profile, roots);
|
|
|
|
CHECK(hasRoot(roots, "main", ProductionRootKind::Main));
|
|
CHECK(hasRoot(roots, "manual", ProductionRootKind::Explicit));
|
|
CHECK(hasRoot(roots, "exported_api", ProductionRootKind::ExportedAbi));
|
|
CHECK(hasRoot(roots, "static_entry", ProductionRootKind::StaticInitializer));
|
|
CHECK(hasRoot(roots, "callback", ProductionRootKind::RegisteredCallback));
|
|
CHECK(hasRoot(roots, "escaped", ProductionRootKind::EscapedAddress));
|
|
CHECK(hasRoot(roots, "Concrete::run", ProductionRootKind::VirtualCandidate));
|
|
CHECK_FALSE(std::ranges::any_of(roots, [](const auto& root) {
|
|
return root.qualified_name == "direct_only" &&
|
|
root.kind == ProductionRootKind::EscapedAddress;
|
|
}));
|
|
|
|
const auto inventory =
|
|
fold::analysis::buildProductionRootInventory(roots, true, profile.profile->entrypoints());
|
|
CHECK_FALSE(inventory.closed_world);
|
|
CHECK(inventory.has_unresolved_dynamic_calls);
|
|
CHECK(inventory.unresolved_explicit_roots.empty());
|
|
CHECK(inventory.production_roots.size() >= 6);
|
|
REQUIRE(inventory.conditional_roots.size() == 1);
|
|
CHECK(inventory.conditional_roots.front().qualified_name == "Concrete::run");
|
|
|
|
const auto missing =
|
|
fold::analysis::buildProductionRootInventory(roots, false, {"missing-entrypoint"});
|
|
CHECK_FALSE(missing.closed_world);
|
|
REQUIRE(missing.unresolved_explicit_roots.size() == 1);
|
|
CHECK(missing.unresolved_explicit_roots.front() == "missing-entrypoint");
|
|
}
|
|
|
|
TEST_CASE("test and benchmark entrypoints stay outside production roots",
|
|
"[production-roots][test-domain]") {
|
|
auto ast = clang::tooling::buildASTFromCodeWithArgs("int main(){ return 0; }", {"-std=c++20"},
|
|
"/project/tests/root_test.cpp");
|
|
REQUIRE(ast);
|
|
std::vector<fold::analysis::ProductionRootRecord> roots;
|
|
fold::analysis::collectProductionRootRecords(ast->getASTContext(), nullptr, roots);
|
|
const auto inventory = fold::analysis::buildProductionRootInventory(roots, false);
|
|
CHECK(inventory.closed_world);
|
|
CHECK(inventory.production_roots.empty());
|
|
REQUIRE(inventory.test_roots.size() == 1);
|
|
CHECK(inventory.test_roots.front().kind == ProductionRootKind::TestEntry);
|
|
|
|
auto third_party = clang::tooling::buildASTFromCodeWithArgs(
|
|
"int main(){ return 0; }", {"-std=c++20"}, "/project/third_party/vendor/main.cpp");
|
|
REQUIRE(third_party);
|
|
roots.clear();
|
|
fold::analysis::collectProductionRootRecords(third_party->getASTContext(), nullptr, roots);
|
|
CHECK(roots.empty());
|
|
}
|