Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2953,49 +2953,89 @@ struct DOTGraphTraits : 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( + 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 PreCallback, + llvm::function_ref PostCallback, + llvm::function_ref 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 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)