#include #include #include #include #include #include "fold/analysis/production_roots.hpp" #include "fold/config/configuration_profile.hpp" namespace { using fold::analysis::ProductionRootKind; bool hasRoot(const std::vector& 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 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 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()); }