Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2953,30 +2953,72 @@ 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 bool nodeHasBugReport(const ExplodedNode *N) { + BugReporter &BR = static_cast( + N->getState()->getStateManager().getOwningEngine())->getBugReporter(); + + const auto EQClasses = + llvm::make_range(BR.EQClasses_begin(), BR.EQClasses_end()); + + for (const auto &EQ : EQClasses) { + for (const BugReport &Report : EQ) { + if (Report.getErrorNode() == N) + return true; + } + } + return false; + } + + static bool nodeOrHiddenPredecessorHasProperty( + const ExplodedNode *N, + llvm::function_ref Property) { + const ExplodedNode *FirstHiddenNode = findFirstHiddenNode(N); + const ExplodedNode *OtherNode = FirstHiddenNode; + while (true) { + if (Property(OtherNode)) + return true; + + if (OtherNode == N) + break; + + OtherNode = *OtherNode->succ_begin(); + } + return false; + } + static std::string getNodeAttributes(const ExplodedNode *N, ExplodedGraph *G) { - if (N->isSink()) - return "color=red"; - return {}; + SmallVector Out; + if (nodeOrHiddenPredecessorHasProperty(N, &nodeHasBugReport)) { + Out.push_back("style=filled"); + Out.push_back("fillcolor=red"); + } + if (nodeOrHiddenPredecessorHasProperty( + N, [](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 const ExplodedNode * findFirstHiddenNode(const ExplodedNode *N) { + const ExplodedNode *FirstHiddenNode = N; + while (FirstHiddenNode->pred_size() == 1 && + isNodeHidden(*FirstHiddenNode->pred_begin())) { + FirstHiddenNode = *FirstHiddenNode->pred_begin(); + } + return FirstHiddenNode; + } + static std::string getNodeLabel(const ExplodedNode *N, ExplodedGraph *G){ std::string sbuf; llvm::raw_string_ostream Out(sbuf); // Find the first node which program point and tag has to be included in // the output. - const ExplodedNode *FirstHiddenNode = N; - while (FirstHiddenNode->pred_size() == 1 && - isNodeHidden(*FirstHiddenNode->pred_begin())) { - FirstHiddenNode = *FirstHiddenNode->pred_begin(); - } + const ExplodedNode *FirstHiddenNode = findFirstHiddenNode(N); ProgramStateRef State = N->getState(); const ASTContext &Context = State->getStateManager().getContext();