#include #include #include #include #include #include "fold/ir/call_graph.hpp" TEST_CASE("project graph keeps overloads distinct by semantic identity", "[call-graph][identity]") { fold::ir::ProjectCallGraph graph; graph.addFunction("usr:f:int", "f", "a.cpp", true, "body-int"); graph.addFunction("usr:f:double", "f", "a.cpp", true, "body-double"); graph.addFunction("usr:caller", "caller", "a.cpp", true); graph.addCallEdge("usr:caller", "usr:f:int"); CHECK(graph.nodeCount() == 3); CHECK(graph.getNode("usr:f:int")->body_hash == "body-int"); CHECK(graph.getNode("usr:f:double")->body_hash == "body-double"); CHECK(graph.getNode("f") == nullptr); // ambiguous names fail closed CHECK(graph.getSccId("f") == std::numeric_limits::max()); CHECK(graph.getCallees("caller") == std::vector{"f"}); CHECK(graph.getCalleeIds("usr:caller") == std::vector{"usr:f:int"}); } TEST_CASE("project graph retains strongest typed edge and deterministic SCC identities", "[call-graph][edge-kind]") { fold::ir::ProjectCallGraph graph; graph.addFunction("usr:a", "a", "a.cpp", true, {}, true); graph.addFunction("usr:b", "b", "b.cpp", true); graph.addFunction("unknown:usr:a", "", {}, false); graph.addCallEdge("usr:a", "usr:b", fold::ir::CallGraphEdgeKind::Conservative, 0.5); graph.addCallEdge("usr:a", "usr:b", fold::ir::CallGraphEdgeKind::Direct, 1.0); graph.addCallEdge("usr:b", "usr:a", fold::ir::CallGraphEdgeKind::Direct, 1.0); graph.addCallEdge("usr:a", "unknown:usr:a", fold::ir::CallGraphEdgeKind::Unknown, 0.0); graph.computeSCCs(); CHECK(graph.edgeCount() == 3); CHECK(graph.edgeCount(fold::ir::CallGraphEdgeKind::Direct) == 2); CHECK(graph.edgeCount(fold::ir::CallGraphEdgeKind::Conservative) == 0); CHECK(graph.edgeCount(fold::ir::CallGraphEdgeKind::Unknown) == 1); CHECK(graph.unresolvedNodeCount() == 1); CHECK(graph.getNode("usr:a")->escapes); const unsigned scc = graph.getSccId("usr:a"); CHECK(scc == graph.getSccId("usr:b")); CHECK(graph.getSccFunctionIds(scc) == std::vector{"usr:a", "usr:b"}); }