struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
DOTGraphTraits (bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
- // FIXME: Since we do not cache error nodes in ExprEngine now, this does not
- // work.
- static std::string getNodeAttributes(const ExplodedNode *N,
- ExplodedGraph *G) {
- if (N->isSink())
- return "color=red";
- return {};
- }
+ static bool nodeHasBugReport(const ExplodedNode *N) {
+ BugReporter &BR = static_cast<ExprEngine *>(
+ N->getState()->getStateManager().getOwningEngine())->getBugReporter();
- static bool isNodeHidden(const ExplodedNode *N) {
- return N->isTrivial();
- }
+ const auto EQClasses =
+ llvm::make_range(BR.EQClasses_begin(), BR.EQClasses_end());
- static std::string getNodeLabel(const ExplodedNode *N, ExplodedGraph *G){
- std::string sbuf;
- llvm::raw_string_ostream Out(sbuf);
+ for (const auto &EQ : EQClasses) {
+ for (const BugReport &Report : EQ) {
+ if (Report.getErrorNode() == N)
+ return true;
+ }
+ }
+ return false;
+ }
- // Find the first node which program point and tag has to be included in
- // the output.
+ /// \p PreCallback: callback before break.
+ /// \p PostCallback: callback after break.
+ /// \p Stop: stop iteration if returns {@code true}
+ /// \return Whether {@code Stop} ever returned {@code true}.
+ static bool traverseHiddenNodes(
+ const ExplodedNode *N,
+ llvm::function_ref<void(const ExplodedNode *)> PreCallback,
+ llvm::function_ref<void(const ExplodedNode *)> PostCallback,
+ llvm::function_ref<bool(const ExplodedNode *)> Stop) {
const ExplodedNode *FirstHiddenNode = N;
while (FirstHiddenNode->pred_size() == 1 &&
isNodeHidden(*FirstHiddenNode->pred_begin())) {
FirstHiddenNode = *FirstHiddenNode->pred_begin();
}
-
- ProgramStateRef State = N->getState();
-
- // Dump program point for all the previously skipped nodes.
const ExplodedNode *OtherNode = FirstHiddenNode;
while (true) {
- OtherNode->getLocation().print(/*CR=*/"\\l", Out);
-
- if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag())
- Out << "\\lTag:" << Tag->getTagDescription();
+ if (Stop(OtherNode))
+ return true;
if (OtherNode == N)
break;
OtherNode = *OtherNode->succ_begin();
+ }
+ return false;
+ }
- Out << "\\l--------\\l";
+ static std::string getNodeAttributes(const ExplodedNode *N,
+ ExplodedGraph *G) {
+ SmallVector<StringRef, 10> Out;
+ auto Noop = [](const ExplodedNode*){};
+ if (traverseHiddenNodes(N, Noop, Noop, &nodeHasBugReport)) {
+ Out.push_back("style=filled");
+ Out.push_back("fillcolor=red");
}
+ if (traverseHiddenNodes(N, Noop, Noop,
+ [](const ExplodedNode *C) { return C->isSink(); }))
+ Out.push_back("color=blue");
+ return llvm::join(Out, ",");
+ }
+
+ static bool isNodeHidden(const ExplodedNode *N) {
+ return N->isTrivial();
+ }
+
+ static std::string getNodeLabel(const ExplodedNode *N, ExplodedGraph *G){
+ std::string sbuf;
+ llvm::raw_string_ostream Out(sbuf);
+
+ ProgramStateRef State = N->getState();
+
+ // Dump program point for all the previously skipped nodes.
+ traverseHiddenNodes(
+ N,
+ [&](const ExplodedNode *OtherNode) {
+ OtherNode->getLocation().print(/*CR=*/"\\l", Out);
+ if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag())
+ Out << "\\lTag:" << Tag->getTagDescription();
+ if (N->isSink())
+ Out << "\\lNode is sink\\l";
+ if (nodeHasBugReport(N))
+ Out << "\\lBug report attached\\l";
+ },
+ [&](const ExplodedNode *OtherNode) { Out << "\\l--------\\l"; },
+ [&](const ExplodedNode *N) { return false; });
+
Out << "\\l\\|";
Out << "StateID: ST" << State->getID() << ", NodeID: N" << N->getID(G)