49#include "llvm/ADT/ArrayRef.h"
50#include "llvm/ADT/DenseMap.h"
51#include "llvm/ADT/DenseSet.h"
52#include "llvm/ADT/FoldingSet.h"
53#include "llvm/ADT/STLExtras.h"
54#include "llvm/ADT/SmallPtrSet.h"
55#include "llvm/ADT/SmallString.h"
56#include "llvm/ADT/SmallVector.h"
57#include "llvm/ADT/Statistic.h"
58#include "llvm/ADT/StringExtras.h"
59#include "llvm/ADT/StringRef.h"
60#include "llvm/ADT/iterator_range.h"
61#include "llvm/Support/Casting.h"
62#include "llvm/Support/Compiler.h"
63#include "llvm/Support/ErrorHandling.h"
64#include "llvm/Support/MemoryBuffer.h"
65#include "llvm/Support/raw_ostream.h"
82#define DEBUG_TYPE "BugReporter"
85 "The maximum number of bug reports in the same equivalence class");
87 "The maximum number of bug reports in the same equivalence class "
88 "where at least one report is valid (not suppressed)");
90STATISTIC(NumTimesReportPassesZ3,
"Number of reports passed Z3");
91STATISTIC(NumTimesReportRefuted,
"Number of reports refuted by Z3");
93 "Number of times a report equivalence class was aborted by the Z3 "
96 "Number of times all reports of an equivalence class was refuted");
100void BugReporterContext::anchor() {}
110 std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>;
114using VisitorsDiagnosticsTy =
115 llvm::DenseMap<const ExplodedNode *, std::vector<PathDiagnosticPieceRef>>;
119using LocationContextMap =
120 llvm::DenseMap<const PathPieces *, const LocationContext *>;
125class PathDiagnosticConstruct {
134 LocationContextMap LCM;
141 CallWithEntryStack CallStack;
145 std::unique_ptr<PathDiagnostic> PD;
151 const Decl *AnalysisEntryPoint);
156 assert(CurrentNode &&
"Already reached the root!");
164 return LCM.find(&PD->getActivePath())->getSecond();
167 const ExplodedNode *getCurrentNode()
const {
return CurrentNode; }
171 bool ascendToPrevNode() {
173 return static_cast<bool>(CurrentNode);
177 return getCurrLocationContext()->getParentMap();
182 const Stmt *getParent(
const Stmt *S)
const {
183 return getParentMap().getParent(S);
192 assert(LCM.count(
Path) &&
193 "Failed to find the context associated with these pieces!");
194 return LCM.find(
Path)->getSecond();
199 PathPieces &getActivePath() {
return PD->getActivePath(); }
200 PathPieces &getMutablePieces() {
return PD->getMutablePieces(); }
203 bool shouldAddControlNotes()
const {
206 bool shouldGenerateDiagnostics()
const {
209 bool supportsLogicalOpControlFlow()
const {
220 std::unique_ptr<const ExplodedGraph> BugPath;
231 std::unique_ptr<const VisitorsDiagnosticsTy> VisitorsDiagnostics;
237 static std::optional<PathDiagnosticBuilder>
241 PathDiagnosticBuilder(
244 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics);
256 std::unique_ptr<PathDiagnostic>
261 const CallWithEntryStack &CallStack)
const;
262 void generatePathDiagnosticsForNode(PathDiagnosticConstruct &
C,
265 void generateMinimalDiagForBlockEdge(PathDiagnosticConstruct &
C,
269 generateDiagForGotoOP(
const PathDiagnosticConstruct &
C,
const Stmt *S,
273 generateDiagForSwitchOP(
const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
277 generateDiagForBinaryOP(
const PathDiagnosticConstruct &
C,
const Stmt *
T,
281 ExecutionContinues(
const PathDiagnosticConstruct &
C)
const;
284 ExecutionContinues(llvm::raw_string_ostream &os,
285 const PathDiagnosticConstruct &
C)
const;
307 const auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
312 for (
auto [Idx, ArgExpr] : llvm::enumerate(CE->arguments())) {
324 if (ArgExpr->getType()->isVoidPointerType())
349 return (llvm::Twine(Msg) +
" via " + std::to_string(ArgIndex) +
350 llvm::getOrdinalSuffix(ArgIndex) +
" parameter").str();
370 if (
X->getTag() == tagPreferred && Y->
getTag() == tagLesser)
373 if (Y->
getTag() == tagPreferred &&
X->getTag() == tagLesser)
385 unsigned N = path.size();
392 for (
unsigned i = 0; i < N; ++i) {
393 auto piece = std::move(path.front());
396 switch (piece->getKind()) {
407 if (
auto *nextEvent =
408 dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
409 auto *
event = cast<PathDiagnosticEventPiece>(piece.get());
413 if (
auto *pieceToKeep =
415 piece = std::move(pieceToKeep == event ? piece : path.front());
427 path.push_back(std::move(piece));
437 bool IsInteresting =
false) {
438 bool containsSomethingInteresting = IsInteresting;
439 const unsigned N = pieces.size();
441 for (
unsigned i = 0 ; i < N ; ++i) {
444 auto piece = std::move(pieces.front());
447 switch (piece->getKind()) {
449 auto &call = cast<PathDiagnosticCallPiece>(*piece);
456 containsSomethingInteresting =
true;
460 auto ¯o = cast<PathDiagnosticMacroPiece>(*piece);
463 containsSomethingInteresting =
true;
467 auto &
event = cast<PathDiagnosticEventPiece>(*piece);
471 containsSomethingInteresting |= !
event.isPrunable();
480 pieces.push_back(std::move(piece));
483 return containsSomethingInteresting;
488 for (
unsigned int i = 0; i <
Path.size(); ++i) {
489 auto Piece = std::move(
Path.front());
491 if (!isa<PathDiagnosticPopUpPiece>(*Piece))
492 Path.push_back(std::move(Piece));
508 for (
const auto &I : Pieces) {
509 auto *
Call = dyn_cast<PathDiagnosticCallPiece>(I.get());
514 if (LastCallLocation) {
516 if (CallerIsImplicit || !
Call->callEnter.asLocation().isValid())
517 Call->callEnter = *LastCallLocation;
518 if (CallerIsImplicit || !
Call->callReturn.asLocation().isValid())
519 Call->callReturn = *LastCallLocation;
525 if (
Call->callEnterWithin.asLocation().isValid() &&
527 ThisCallLocation = &
Call->callEnterWithin;
529 ThisCallLocation = &
Call->callEnter;
531 assert(ThisCallLocation &&
"Outermost call has an invalid location");
540 for (PathPieces::iterator I = Pieces.begin(),
E = Pieces.end(); I !=
E;) {
541 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
544 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
547 if (
auto *
CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) {
548 const Stmt *Start =
CF->getStartLocation().asStmt();
549 const Stmt *End =
CF->getEndLocation().asStmt();
550 if (isa_and_nonnull<CXXDefaultInitExpr>(Start)) {
553 }
else if (isa_and_nonnull<CXXDefaultInitExpr>(End)) {
554 PathPieces::iterator Next = std::next(I);
557 dyn_cast<PathDiagnosticControlFlowPiece>(Next->get())) {
558 NextCF->setStartLocation(
CF->getStartLocation());
574 for (PathPieces::iterator I = Pieces.begin(),
E = Pieces.end(); I !=
E;) {
575 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
578 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
581 if (!(*I)->getLocation().isValid() ||
582 !(*I)->getLocation().asLocation().isValid()) {
591 const PathDiagnosticConstruct &
C)
const {
592 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
594 C.getCurrLocationContext());
601 llvm::raw_string_ostream &os,
const PathDiagnosticConstruct &
C)
const {
603 if (os.str().empty())
609 os <<
"Execution continues on line "
610 << getSourceManager().getExpansionLineNumber(
Loc.asLocation())
613 os <<
"Execution jumps to the end of the ";
614 const Decl *
D =
C.getCurrLocationContext()->getDecl();
615 if (isa<ObjCMethodDecl>(
D))
617 else if (isa<FunctionDecl>(
D))
620 assert(isa<BlockDecl>(
D));
621 os <<
"anonymous block";
637 switch (
Parent->getStmtClass()) {
638 case Stmt::ForStmtClass:
639 case Stmt::DoStmtClass:
640 case Stmt::WhileStmtClass:
641 case Stmt::ObjCForCollectionStmtClass:
642 case Stmt::CXXForRangeStmtClass:
653 bool allowNestedContexts =
false) {
660 switch (
Parent->getStmtClass()) {
661 case Stmt::BinaryOperatorClass: {
662 const auto *B = cast<BinaryOperator>(
Parent);
663 if (B->isLogicalOp())
667 case Stmt::CompoundStmtClass:
668 case Stmt::StmtExprClass:
670 case Stmt::ChooseExprClass:
673 if (allowNestedContexts || cast<ChooseExpr>(
Parent)->getCond() == S)
677 case Stmt::BinaryConditionalOperatorClass:
678 case Stmt::ConditionalOperatorClass:
681 if (allowNestedContexts ||
682 cast<AbstractConditionalOperator>(
Parent)->getCond() == S)
686 case Stmt::CXXForRangeStmtClass:
687 if (cast<CXXForRangeStmt>(
Parent)->getBody() == S)
690 case Stmt::DoStmtClass:
692 case Stmt::ForStmtClass:
693 if (cast<ForStmt>(
Parent)->getBody() == S)
696 case Stmt::IfStmtClass:
697 if (cast<IfStmt>(
Parent)->getCond() != S)
700 case Stmt::ObjCForCollectionStmtClass:
701 if (cast<ObjCForCollectionStmt>(
Parent)->getBody() == S)
704 case Stmt::WhileStmtClass:
705 if (cast<WhileStmt>(
Parent)->getCond() != S)
715 assert(S &&
"Cannot have null Stmt for PathDiagnosticLocation");
734void PathDiagnosticBuilder::updateStackPiecesWithMessage(
736 if (R->hasCallStackHint(
P))
737 for (
const auto &I : CallStack) {
740 std::string stackMsg = R->getCallStackMessage(
P, N);
754 const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
760 llvm::raw_string_ostream os(sbuf);
766 switch (S->getStmtClass()) {
768 os <<
"No cases match in the switch statement. "
769 "Control jumps to line "
770 << End.asLocation().getExpansionLineNumber();
772 case Stmt::DefaultStmtClass:
773 os <<
"Control jumps to the 'default' case at line "
774 << End.asLocation().getExpansionLineNumber();
777 case Stmt::CaseStmtClass: {
778 os <<
"Control jumps to 'case ";
779 const auto *Case = cast<CaseStmt>(S);
783 bool GetRawInt =
true;
785 if (
const auto *DR = dyn_cast<DeclRefExpr>(LHS)) {
788 const auto *
D = dyn_cast<EnumConstantDecl>(DR->getDecl());
799 os <<
":' at line " << End.asLocation().getExpansionLineNumber();
804 os <<
"'Default' branch taken. ";
805 End = ExecutionContinues(os,
C);
807 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
812 const PathDiagnosticConstruct &
C,
const Stmt *S,
815 llvm::raw_string_ostream os(sbuf);
818 os <<
"Control jumps to line " << End.asLocation().getExpansionLineNumber();
819 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str());
823 const PathDiagnosticConstruct &
C,
const Stmt *
T,
const CFGBlock *Src,
828 const auto *B = cast<BinaryOperator>(
T);
830 llvm::raw_string_ostream os(sbuf);
831 os <<
"Left side of '";
834 if (B->getOpcode() == BO_LAnd) {
847 End = ExecutionContinues(
C);
850 assert(B->getOpcode() == BO_LOr);
858 End = ExecutionContinues(
C);
866 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
870void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge(
871 PathDiagnosticConstruct &
C,
BlockEdge BE)
const {
881 switch (
T->getStmtClass()) {
885 case Stmt::GotoStmtClass:
886 case Stmt::IndirectGotoStmtClass: {
887 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
888 C.getActivePath().push_front(generateDiagForGotoOP(
C, S, Start));
892 case Stmt::SwitchStmtClass: {
893 C.getActivePath().push_front(generateDiagForSwitchOP(
C, Dst, Start));
897 case Stmt::BreakStmtClass:
898 case Stmt::ContinueStmtClass: {
900 llvm::raw_string_ostream os(sbuf);
902 C.getActivePath().push_front(
903 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
908 case Stmt::BinaryConditionalOperatorClass:
909 case Stmt::ConditionalOperatorClass: {
911 llvm::raw_string_ostream os(sbuf);
912 os <<
"'?' condition is ";
921 if (
const Stmt *S = End.asStmt())
924 C.getActivePath().push_front(
925 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
930 case Stmt::BinaryOperatorClass: {
931 if (!
C.supportsLogicalOpControlFlow())
934 C.getActivePath().push_front(generateDiagForBinaryOP(
C,
T, Src, Dst));
938 case Stmt::DoStmtClass:
941 llvm::raw_string_ostream os(sbuf);
943 os <<
"Loop condition is true. ";
946 if (
const Stmt *S = End.asStmt())
949 C.getActivePath().push_front(
950 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
955 if (
const Stmt *S = End.asStmt())
958 C.getActivePath().push_front(
959 std::make_shared<PathDiagnosticControlFlowPiece>(
960 Start, End,
"Loop condition is false. Exiting loop"));
964 case Stmt::WhileStmtClass:
965 case Stmt::ForStmtClass:
968 llvm::raw_string_ostream os(sbuf);
970 os <<
"Loop condition is false. ";
972 if (
const Stmt *S = End.asStmt())
975 C.getActivePath().push_front(
976 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
980 if (
const Stmt *S = End.asStmt())
983 C.getActivePath().push_front(
984 std::make_shared<PathDiagnosticControlFlowPiece>(
985 Start, End,
"Loop condition is true. Entering loop body"));
990 case Stmt::IfStmtClass: {
993 if (
const Stmt *S = End.asStmt())
997 C.getActivePath().push_front(
998 std::make_shared<PathDiagnosticControlFlowPiece>(
999 Start, End,
"Taking false branch"));
1001 C.getActivePath().push_front(
1002 std::make_shared<PathDiagnosticControlFlowPiece>(
1003 Start, End,
"Taking true branch"));
1016 case Stmt::ForStmtClass:
1017 case Stmt::WhileStmtClass:
1018 case Stmt::ObjCForCollectionStmtClass:
1019 case Stmt::CXXForRangeStmtClass:
1048 const Stmt *S = SP->getStmt();
1058 const Stmt *LoopBody =
nullptr;
1060 case Stmt::CXXForRangeStmtClass: {
1061 const auto *FR = cast<CXXForRangeStmt>(Term);
1066 LoopBody = FR->getBody();
1069 case Stmt::ForStmtClass: {
1070 const auto *FS = cast<ForStmt>(Term);
1073 LoopBody = FS->getBody();
1076 case Stmt::ObjCForCollectionStmtClass: {
1077 const auto *FC = cast<ObjCForCollectionStmt>(Term);
1078 LoopBody = FC->getBody();
1081 case Stmt::WhileStmtClass:
1082 LoopBody = cast<WhileStmt>(Term)->getBody();
1112 std::make_shared<PathDiagnosticControlFlowPiece>(NewLoc, PrevLoc));
1120 if (
const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S))
1121 return FS->getElement();
1128 "Loop body skipped when range is empty";
1130 "Loop body skipped when collection is empty";
1132static std::unique_ptr<FilesToLineNumsMap>
1135void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
1146 if (
C.shouldAddPathEdges()) {
1162 bool VisitedEntireCall =
C.PD->isWithinCall();
1163 C.PD->popActivePath();
1166 if (VisitedEntireCall) {
1167 Call = cast<PathDiagnosticCallPiece>(
C.getActivePath().front().get());
1171 const Decl *Caller = CE->getLocationContext()->getDecl();
1173 assert(
C.getActivePath().size() == 1 &&
1174 C.getActivePath().front().get() ==
Call);
1178 assert(
C.isInLocCtxMap(&
C.getActivePath()) &&
1179 "When we ascend to a previously unvisited call, the active path's "
1180 "address shouldn't change, but rather should be compacted into "
1181 "a single CallEvent!");
1182 C.updateLocCtxMap(&
C.getActivePath(),
C.getCurrLocationContext());
1185 assert(!
C.isInLocCtxMap(&
Call->path) &&
1186 "When we ascend to a previously unvisited call, this must be the "
1187 "first time we encounter the caller context!");
1188 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1190 Call->setCallee(*CE,
SM);
1193 PrevLoc =
Call->getLocation();
1195 if (!
C.CallStack.empty()) {
1196 assert(
C.CallStack.back().first ==
Call);
1197 C.CallStack.pop_back();
1202 assert(
C.getCurrLocationContext() ==
C.getLocationContextForActivePath() &&
1203 "The current position in the bug path is out of sync with the "
1204 "location context associated with the active path!");
1207 if (std::optional<CallExitEnd> CE =
P.getAs<
CallExitEnd>()) {
1213 assert(!
C.isInLocCtxMap(&
Call->path) &&
1214 "We just entered a call, this must've been the first time we "
1215 "encounter its context!");
1216 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1218 if (
C.shouldAddPathEdges()) {
1224 auto *
P =
Call.get();
1225 C.getActivePath().push_front(std::move(
Call));
1228 C.PD->pushActivePath(&
P->path);
1229 C.CallStack.push_back(CallWithEntry(
P,
C.getCurrentNode()));
1234 if (!
C.shouldAddPathEdges())
1240 if (!isa<ObjCForCollectionStmt>(PS->getStmt())) {
1248 if (
C.shouldAddControlNotes()) {
1249 generateMinimalDiagForBlockEdge(
C, *BE);
1252 if (!
C.shouldAddPathEdges()) {
1259 const Stmt *Body =
nullptr;
1261 if (
const auto *FS = dyn_cast<ForStmt>(
Loop))
1262 Body = FS->getBody();
1263 else if (
const auto *WS = dyn_cast<WhileStmt>(
Loop))
1264 Body = WS->getBody();
1265 else if (
const auto *OFS = dyn_cast<ObjCForCollectionStmt>(
Loop)) {
1266 Body = OFS->getBody();
1267 }
else if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(
Loop)) {
1268 Body = FRS->getBody();
1272 auto p = std::make_shared<PathDiagnosticEventPiece>(
1273 L,
"Looping back to the head of the loop");
1274 p->setPrunable(
true);
1278 if (!
C.shouldAddControlNotes()) {
1279 C.getActivePath().push_front(std::move(p));
1282 if (
const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
1302 if (!IsInLoopBody) {
1303 if (isa<ObjCForCollectionStmt>(Term)) {
1305 }
else if (isa<CXXForRangeStmt>(Term)) {
1317 C.getCurrLocationContext());
1318 auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
1319 PE->setPrunable(
true);
1323 if (!
C.shouldAddControlNotes()) {
1324 C.getActivePath().push_front(std::move(PE));
1327 }
else if (isa<BreakStmt, ContinueStmt, GotoStmt>(Term)) {
1335static std::unique_ptr<PathDiagnostic>
1337 const Decl *AnalysisEntryPoint) {
1339 return std::make_unique<PathDiagnostic>(
1343 AnalysisEntryPoint, std::make_unique<FilesToLineNumsMap>());
1346static std::unique_ptr<PathDiagnostic>
1349 const Decl *AnalysisEntryPoint) {
1351 return std::make_unique<PathDiagnostic>(
1368 if (isa<FullExpr, CXXBindTemporaryExpr, SubstNonTypeTemplateParmExpr>(S))
1378 switch (S->getStmtClass()) {
1379 case Stmt::BinaryOperatorClass: {
1380 const auto *BO = cast<BinaryOperator>(S);
1381 if (!BO->isLogicalOp())
1383 return BO->getLHS() == Cond || BO->getRHS() == Cond;
1385 case Stmt::IfStmtClass:
1386 return cast<IfStmt>(S)->getCond() == Cond;
1387 case Stmt::ForStmtClass:
1388 return cast<ForStmt>(S)->getCond() == Cond;
1389 case Stmt::WhileStmtClass:
1390 return cast<WhileStmt>(S)->getCond() == Cond;
1391 case Stmt::DoStmtClass:
1392 return cast<DoStmt>(S)->getCond() == Cond;
1393 case Stmt::ChooseExprClass:
1394 return cast<ChooseExpr>(S)->getCond() == Cond;
1395 case Stmt::IndirectGotoStmtClass:
1396 return cast<IndirectGotoStmt>(S)->getTarget() == Cond;
1397 case Stmt::SwitchStmtClass:
1398 return cast<SwitchStmt>(S)->getCond() == Cond;
1399 case Stmt::BinaryConditionalOperatorClass:
1400 return cast<BinaryConditionalOperator>(S)->getCond() == Cond;
1401 case Stmt::ConditionalOperatorClass: {
1402 const auto *CO = cast<ConditionalOperator>(S);
1403 return CO->getCond() == Cond ||
1404 CO->getLHS() == Cond ||
1405 CO->getRHS() == Cond;
1407 case Stmt::ObjCForCollectionStmtClass:
1408 return cast<ObjCForCollectionStmt>(S)->getElement() == Cond;
1409 case Stmt::CXXForRangeStmtClass: {
1410 const auto *FRS = cast<CXXForRangeStmt>(S);
1411 return FRS->getCond() == Cond || FRS->getRangeInit() == Cond;
1419 if (
const auto *FS = dyn_cast<ForStmt>(FL))
1420 return FS->getInc() == S || FS->getInit() == S;
1421 if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(FL))
1422 return FRS->getInc() == S || FRS->getRangeStmt() == S ||
1423 FRS->getLoopVarStmt() || FRS->getRangeInit() == S;
1436 PathPieces::iterator Prev = pieces.end();
1437 for (PathPieces::iterator I = pieces.begin(),
E = Prev; I !=
E;
1439 auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1448 const Stmt *InnerStmt =
nullptr;
1449 while (NextSrcContext.
isValid() && NextSrcContext.
asStmt() != InnerStmt) {
1450 SrcContexts.push_back(NextSrcContext);
1451 InnerStmt = NextSrcContext.
asStmt();
1460 const Stmt *Dst = Piece->getEndLocation().getStmtOrNull();
1470 if (llvm::is_contained(SrcContexts, DstContext))
1474 Piece->setStartLocation(DstContext);
1479 auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get());
1482 if (
const Stmt *PrevSrc =
1483 PrevPiece->getStartLocation().getStmtOrNull()) {
1485 if (PrevSrcParent ==
1487 PrevPiece->setEndLocation(DstContext);
1498 std::make_shared<PathDiagnosticControlFlowPiece>(SrcLoc, DstContext);
1500 I = pieces.insert(I, std::move(
P));
1516 for (PathPieces::iterator I = pieces.begin(),
E = pieces.end(); I !=
E; ++I) {
1517 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1522 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1523 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1525 if (!s1Start || !s1End)
1528 PathPieces::iterator NextI = I; ++NextI;
1538 const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1540 StringRef S = EV->getString();
1549 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1559 if (!s2Start || !s2End || s1End != s2Start)
1575 I = pieces.erase(I);
1588 SM.getExpansionRange(
Range.getEnd()).getEnd());
1591 if (FID !=
SM.getFileID(ExpansionRange.
getEnd()))
1592 return std::nullopt;
1594 std::optional<MemoryBufferRef> Buffer =
SM.getBufferOrNone(FID);
1596 return std::nullopt;
1598 unsigned BeginOffset =
SM.getFileOffset(ExpansionRange.
getBegin());
1599 unsigned EndOffset =
SM.getFileOffset(ExpansionRange.
getEnd());
1600 StringRef Snippet = Buffer->getBuffer().slice(BeginOffset, EndOffset);
1606 if (Snippet.find_first_of(
"\r\n") != StringRef::npos)
1607 return std::nullopt;
1610 return Snippet.size();
1636 for (PathPieces::iterator I =
Path.begin(),
E =
Path.end(); I !=
E; ) {
1638 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1645 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1646 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1648 PathPieces::iterator NextI = I; ++NextI;
1652 const auto *PieceNextI =
1653 dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1656 if (isa<PathDiagnosticEventPiece>(NextI->get())) {
1660 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1669 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1670 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1672 if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
1673 const size_t MAX_SHORT_LINE_LENGTH = 80;
1675 if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
1677 if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
1679 I =
Path.erase(NextI);
1702 bool erased =
false;
1704 for (PathPieces::iterator I = path.begin(),
E = path.end(); I !=
E;
1708 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1713 const Stmt *start = PieceI->getStartLocation().getStmtOrNull();
1714 const Stmt *end = PieceI->getEndLocation().getStmtOrNull();
1729 if (!
SM.isWrittenInSameFile(FirstLoc, SecondLoc))
1731 if (
SM.isBeforeInTranslationUnit(SecondLoc, FirstLoc))
1732 std::swap(SecondLoc, FirstLoc);
1741 const size_t MAX_PUNY_EDGE_LENGTH = 2;
1742 if (*ByteWidth <= MAX_PUNY_EDGE_LENGTH) {
1754 for (PathPieces::iterator I = path.begin(),
E = path.end(); I !=
E; ++I) {
1755 const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get());
1760 PathPieces::iterator NextI = I; ++NextI;
1764 const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1770 if (PieceI->getString() == PieceNextI->getString()) {
1778 bool hasChanges =
false;
1784 for (PathPieces::iterator I = path.begin(),
E = path.end(); I !=
E; ) {
1786 if (
auto *CallI = dyn_cast<PathDiagnosticCallPiece>(I->get())) {
1789 if (!OCS.count(CallI)) {
1799 auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1806 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1807 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1811 PathPieces::iterator NextI = I; ++NextI;
1815 const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1822 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1823 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1841 if (level1 && level1 == level2 && level1 == level3 && level1 == level4) {
1842 PieceI->setEndLocation(PieceNextI->getEndLocation());
1855 if (s1End && s1End == s2Start && level2) {
1856 bool removeEdge =
false;
1882 else if (s1Start && s2End &&
1895 else if (s1Start && s2End &&
1897 SourceRange EdgeRange(PieceI->getEndLocation().asLocation(),
1898 PieceI->getStartLocation().asLocation());
1905 PieceI->setEndLocation(PieceNextI->getEndLocation());
1919 if (s1End == s2Start) {
1920 const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3);
1921 if (FS && FS->getCollection()->IgnoreParens() == s2Start &&
1922 s2End == FS->getElement()) {
1923 PieceI->setEndLocation(PieceNextI->getEndLocation());
1960 const auto *FirstEdge =
1961 dyn_cast<PathDiagnosticControlFlowPiece>(
Path.front().get());
1965 const Decl *
D =
C.getLocationContextFor(&
Path)->getDecl();
1968 if (FirstEdge->getStartLocation() != EntryLoc)
1980 for (
const auto &
P : path) {
1983 unsigned LineNo =
Loc.getLineNumber();
1985 ExecutedLines[FID].insert(LineNo);
1989PathDiagnosticConstruct::PathDiagnosticConstruct(
1992 : Consumer(PDC), CurrentNode(ErrorNode),
1993 SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
1995 AnalysisEntryPoint)) {
1999PathDiagnosticBuilder::PathDiagnosticBuilder(
2002 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics)
2004 ErrorNode(ErrorNode),
2005 VisitorsDiagnostics(
std::move(VisitorsDiagnostics)) {}
2007std::unique_ptr<PathDiagnostic>
2009 const Decl *EntryPoint = getBugReporter().getAnalysisEntryPoint();
2010 PathDiagnosticConstruct Construct(PDC, ErrorNode, R, EntryPoint);
2019 auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
2021 if (EndNotes != VisitorsDiagnostics->end()) {
2022 assert(!EndNotes->second.empty());
2023 LastPiece = EndNotes->second[0];
2028 Construct.PD->setEndOfPath(LastPiece);
2033 while (Construct.ascendToPrevNode()) {
2034 generatePathDiagnosticsForNode(Construct, PrevLoc);
2036 auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode());
2037 if (VisitorNotes == VisitorsDiagnostics->end())
2042 std::set<llvm::FoldingSetNodeID> DeduplicationSet;
2046 llvm::FoldingSetNodeID
ID;
2048 if (!DeduplicationSet.insert(ID).second)
2053 updateStackPiecesWithMessage(
Note, Construct.CallStack);
2054 Construct.getActivePath().push_front(
Note);
2062 Construct.getLocationContextForActivePath()->
getStackFrame();
2070 if (!Construct.PD->path.empty()) {
2072 bool stillHasNotes =
2074 assert(stillHasNotes);
2075 (void)stillHasNotes;
2079 if (!Opts.ShouldAddPopUpNotes)
2092 while (
optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) {
2107 if (Opts.ShouldDisplayMacroExpansions)
2110 return std::move(Construct.PD);
2117void BugType::anchor() {}
2123LLVM_ATTRIBUTE_USED
static bool
2125 for (
const std::pair<StringRef, StringRef> &Pair : Registry.
Dependencies) {
2126 if (Pair.second == CheckerName)
2133 StringRef CheckerName) {
2135 if (
Checker.FullName == CheckerName)
2139 "Checker name not found in CheckerRegistry -- did you retrieve it "
2140 "correctly from CheckerManager::getCurrentCheckerName?");
2144 const BugType &bt, StringRef shortDesc, StringRef desc,
2146 const Decl *DeclToUnique)
2147 :
BugReport(
Kind::PathSensitive, bt, shortDesc, desc), ErrorNode(errorNode),
2148 ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() :
SourceRange()),
2149 UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) {
2151 ->getAnalysisManager()
2152 .getCheckerManager()
2153 ->getCheckerRegistryData(),
2155 "Some checkers depend on this one! We don't allow dependency "
2156 "checkers to emit warnings, because checkers should depend on "
2157 "*modeling*, not *diagnostics*.");
2161 ->getAnalysisManager()
2162 .getCheckerManager()
2163 ->getCheckerRegistryData(),
2165 "Hidden checkers musn't emit diagnostics as they are by definition "
2166 "non-user facing!");
2170 std::unique_ptr<BugReporterVisitor> visitor) {
2174 llvm::FoldingSetNodeID ID;
2175 visitor->Profile(ID);
2177 void *InsertPos =
nullptr;
2182 Callbacks.push_back(std::move(visitor));
2199 hash.AddInteger(
static_cast<int>(
getKind()));
2200 hash.AddPointer(&
BT);
2206 if (!range.isValid())
2208 hash.Add(range.getBegin());
2209 hash.Add(range.getEnd());
2214 hash.AddInteger(
static_cast<int>(
getKind()));
2215 hash.AddPointer(&
BT);
2229 if (!range.isValid())
2231 hash.Add(range.getBegin());
2232 hash.Add(range.getEnd());
2238 llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap,
T Val,
2240 auto Result = InterestingnessMap.insert({Val, TKind});
2260 "BugReport::markInteresting currently can only handle 2 different "
2261 "tracking kinds! Please define what tracking kind should this entitiy"
2262 "have, if it was already marked as interesting with a different kind!");
2274 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2286 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2298 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2309 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2325std::optional<bugreporter::TrackingKind>
2344 "BugReport::getInterestingnessKind currently can only handle 2 different "
2345 "tracking kinds! Please define what tracking kind should we return here "
2346 "when the kind of getAsRegion() and getAsSymbol() is different!");
2347 return std::nullopt;
2350std::optional<bugreporter::TrackingKind>
2353 return std::nullopt;
2358 return std::nullopt;
2359 return It->getSecond();
2362std::optional<bugreporter::TrackingKind>
2365 return std::nullopt;
2370 return It->getSecond();
2372 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2374 return std::nullopt;
2400 const Stmt *S =
nullptr;
2404 if (BE->getBlock() == &Exit)
2425 assert(
ErrorNode &&
"Cannot create a location with a null node.");
2447 if (
const auto *AS = dyn_cast<AttributedStmt>(S))
2448 S = AS->getSubStmt();
2451 if (
const auto *ME = dyn_cast<MemberExpr>(S))
2455 if (
const auto *B = dyn_cast<BinaryOperator>(S))
2461 if (S->getBeginLoc().isValid())
2485 :
D(
D), UserSuppressions(
D.getASTContext()) {}
2489 assert(StrBugTypes.empty() &&
2490 "Destroying BugReporter before diagnostics are emitted!");
2493 for (
const auto I : EQClassesVector)
2500 for (
const auto EQ : EQClassesVector)
2507 StrBugTypes.clear();
2520 std::unique_ptr<ExplodedGraph> BugPath;
2527class BugPathGetter {
2528 std::unique_ptr<ExplodedGraph> TrimmedGraph;
2530 using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
2533 PriorityMapTy PriorityMap;
2537 using ReportNewNodePair =
2538 std::pair<PathSensitiveBugReport *, const ExplodedNode *>;
2541 BugPathInfo CurrentBugPath;
2544 template <
bool Descending>
2545 class PriorityCompare {
2546 const PriorityMapTy &PriorityMap;
2549 PriorityCompare(
const PriorityMapTy &M) : PriorityMap(M) {}
2552 PriorityMapTy::const_iterator LI = PriorityMap.find(LHS);
2553 PriorityMapTy::const_iterator RI = PriorityMap.find(RHS);
2554 PriorityMapTy::const_iterator
E = PriorityMap.end();
2561 return Descending ? LI->second > RI->second
2562 : LI->second < RI->second;
2565 bool operator()(
const ReportNewNodePair &LHS,
2566 const ReportNewNodePair &RHS)
const {
2567 return (*
this)(LHS.second, RHS.second);
2575 BugPathInfo *getNextBugPath();
2580BugPathGetter::BugPathGetter(
const ExplodedGraph *OriginalGraph,
2583 for (
const auto I : bugReports) {
2584 assert(I->isValid() &&
2585 "We only allow BugReporterVisitors and BugReporter itself to "
2586 "invalidate reports!");
2587 Nodes.emplace_back(I->getErrorNode());
2593 TrimmedGraph = OriginalGraph->
trim(
Nodes, &ForwardMap);
2601 const ExplodedNode *NewNode = ForwardMap.lookup(Report->getErrorNode());
2603 "Failed to construct a trimmed graph that contains this error "
2605 ReportNodes.emplace_back(Report, NewNode);
2606 RemainingNodes.insert(NewNode);
2609 assert(!RemainingNodes.empty() &&
"No error node found in the trimmed graph");
2612 std::queue<const ExplodedNode *> WS;
2614 assert(TrimmedGraph->num_roots() == 1);
2615 WS.push(*TrimmedGraph->roots_begin());
2618 while (!WS.empty()) {
2622 PriorityMapTy::iterator PriorityEntry;
2624 std::tie(PriorityEntry, IsNew) = PriorityMap.insert({
Node,
Priority});
2628 assert(PriorityEntry->second <= Priority);
2632 if (RemainingNodes.erase(
Node))
2633 if (RemainingNodes.empty())
2641 llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap));
2644BugPathInfo *BugPathGetter::getNextBugPath() {
2645 if (ReportNodes.empty())
2649 std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val();
2650 assert(PriorityMap.contains(OrigN) &&
"error node not accessible from root");
2654 auto GNew = std::make_unique<ExplodedGraph>();
2670 CurrentBugPath.ErrorNode = NewN;
2676 GNew->addRoot(NewN);
2683 PriorityCompare<false>(PriorityMap));
2686 CurrentBugPath.BugPath = std::move(GNew);
2688 return &CurrentBugPath;
2695 using MacroStackTy = std::vector<
2696 std::pair<std::shared_ptr<PathDiagnosticMacroPiece>,
SourceLocation>>;
2698 using PiecesTy = std::vector<PathDiagnosticPieceRef>;
2700 MacroStackTy MacroStack;
2703 for (PathPieces::const_iterator I = path.begin(),
E = path.end();
2705 const auto &piece = *I;
2708 if (
auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) {
2718 SM.getExpansionLoc(
Loc) :
2721 if (
Loc.isFileID()) {
2723 Pieces.push_back(piece);
2727 assert(
Loc.isMacroID());
2730 if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
2731 MacroStack.back().first->subPieces.push_back(piece);
2737 std::shared_ptr<PathDiagnosticMacroPiece> MacroGroup;
2740 SM.getExpansionLoc(
Loc) :
2744 while (!MacroStack.empty()) {
2745 if (InstantiationLoc == MacroStack.back().second) {
2746 MacroGroup = MacroStack.back().first;
2750 if (ParentInstantiationLoc == MacroStack.back().second) {
2751 MacroGroup = MacroStack.back().first;
2755 MacroStack.pop_back();
2758 if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
2760 auto NewGroup = std::make_shared<PathDiagnosticMacroPiece>(
2764 MacroGroup->subPieces.push_back(NewGroup);
2766 assert(InstantiationLoc.
isFileID());
2767 Pieces.push_back(NewGroup);
2770 MacroGroup = NewGroup;
2771 MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
2775 MacroGroup->subPieces.push_back(piece);
2781 path.insert(path.end(), Pieces.begin(), Pieces.end());
2787static std::unique_ptr<VisitorsDiagnosticsTy>
2791 std::unique_ptr<VisitorsDiagnosticsTy> Notes =
2792 std::make_unique<VisitorsDiagnosticsTy>();
2805 for (std::unique_ptr<BugReporterVisitor> &Visitor : R->
visitors())
2806 visitors.push_back(std::move(Visitor));
2813 for (
auto &
V : visitors) {
2814 V->finalizeVisitor(BRC, ErrorNode, *R);
2816 if (
auto Piece =
V->getEndPath(BRC, ErrorNode, *R)) {
2817 assert(!LastPiece &&
2818 "There can only be one final piece in a diagnostic.");
2820 "The final piece must contain a message!");
2821 LastPiece = std::move(Piece);
2822 (*Notes)[ErrorNode].push_back(LastPiece);
2828 for (
auto &
V : visitors) {
2829 auto P =
V->VisitNode(NextNode, BRC, *R);
2831 (*Notes)[NextNode].push_back(std::move(
P));
2843std::optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
2848 BugPathGetter BugGraph(&Reporter.
getGraph(), bugReports);
2850 while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) {
2853 assert(R &&
"No original report found for sliced graph.");
2854 assert(R->
isValid() &&
"Report selected by trimmed graph marked invalid.");
2869 std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes =
2884 switch (Z3Oracle.interpretQueryResult(CrosscheckResult)) {
2886 ++NumTimesReportRefuted;
2887 R->
markInvalid(
"Infeasible constraints",
nullptr);
2890 ++NumTimesReportEQClassAborted;
2893 ++NumTimesReportPassesZ3;
2899 return PathDiagnosticBuilder(std::move(BRC), std::move(BugPath->BugPath),
2900 BugPath->Report, BugPath->ErrorNode,
2901 std::move(visitorNotes));
2905 ++NumTimesReportEQClassWasExhausted;
2909std::unique_ptr<DiagnosticForConsumerMapTy>
2913 assert(!bugReports.empty());
2915 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
2917 std::optional<PathDiagnosticBuilder> PDB =
2918 PathDiagnosticBuilder::findValidReport(bugReports, *
this);
2922 if (std::unique_ptr<PathDiagnostic> PD = PDB->generate(PC)) {
2923 (*Out)[PC] = std::move(PD);
2932 bool ValidSourceLoc = R->getLocation().isValid();
2933 assert(ValidSourceLoc);
2936 if (!ValidSourceLoc)
2944 llvm::FoldingSetNodeID ID;
2953 EQClasses.InsertNode(EQ, InsertPos);
2954 EQClassesVector.push_back(EQ);
2956 EQ->AddReport(std::move(R));
2960 if (
auto PR = dyn_cast<PathSensitiveBugReport>(R.get()))
2964 assert((
E->isSink() ||
E->getLocation().getTag()) &&
2965 "Error node must either be a sink or have a tag");
2968 E->getLocationContext()->getAnalysisDeclContext();
2987struct FRIEC_WLItem {
2992 : N(n), I(N->succ_begin()),
E(N->succ_end()) {}
2997BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass(
3002 assert(
EQ.getReports().size() > 0);
3003 const BugType& BT =
EQ.getReports()[0]->getBugType();
3006 for (
auto &J :
EQ.getReports()) {
3007 if (
auto *PR = dyn_cast<PathSensitiveBugReport>(J.get())) {
3009 bugReports.push_back(PR);
3023 for (
const auto &I:
EQ.getReports()) {
3024 auto *R = dyn_cast<PathSensitiveBugReport>(I.get());
3029 if (errorNode->
isSink()) {
3031 "BugType::isSuppressSink() should not be 'true' for sink end nodes");
3035 bugReports.push_back(R);
3046 if (ErrorB->isInevitablySinking())
3051 using WLItem = FRIEC_WLItem;
3054 llvm::DenseMap<const ExplodedNode *, unsigned>
Visited;
3057 WL.push_back(errorNode);
3060 while (!WL.empty()) {
3061 WLItem &WI = WL.back();
3062 assert(!WI.N->succ_empty());
3064 for (; WI.I != WI.E; ++WI.I) {
3070 bugReports.push_back(R);
3081 unsigned &mark =
Visited[Succ];
3091 if (!WL.empty() && &WL.back() == &WI)
3098 return exampleReport;
3103 BugReport *report = findReportInEquivalenceClass(EQ, bugReports);
3108 for (
const std::string &CheckerOrPackage :
3115 std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics =
3118 for (
auto &
P : *Diagnostics) {
3120 std::unique_ptr<PathDiagnostic> &PD =
P.second;
3124 if (PD->path.empty()) {
3126 auto piece = std::make_unique<PathDiagnosticEventPiece>(
3129 piece->addRange(
Range);
3130 PD->setEndOfPath(std::move(piece));
3137 for (
const auto &I : llvm::reverse(report->
getNotes())) {
3139 auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
3142 ConvertedPiece->addRange(R);
3144 Pieces.push_front(std::move(ConvertedPiece));
3147 for (
const auto &I : llvm::reverse(report->
getNotes()))
3148 Pieces.push_front(I);
3151 for (
const auto &I : report->
getFixits())
3152 Pieces.back()->addFixit(I);
3160 Pieces.push_front(std::make_shared<PathDiagnosticEventPiece>(
3162 "[debug] analyzing from " +
3176 if (
const auto FD = dyn_cast<FunctionDecl>(Signature)) {
3177 SignatureSourceRange = FD->getSourceRange();
3178 }
else if (
const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) {
3179 SignatureSourceRange = OD->getSourceRange();
3185 : SignatureSourceRange.
getEnd();
3186 if (!Start.
isValid() || !End.isValid())
3188 unsigned StartLine =
SM.getExpansionLineNumber(Start);
3189 unsigned EndLine =
SM.getExpansionLineNumber(End);
3191 FileID FID =
SM.getFileID(
SM.getExpansionLoc(Start));
3192 for (
unsigned Line = StartLine;
Line <= EndLine;
Line++)
3193 ExecutedLines[FID].insert(
Line);
3203 FileID FID =
SM.getFileID(ExpansionLoc);
3204 unsigned LineNo =
SM.getExpansionLineNumber(ExpansionLoc);
3205 ExecutedLines[FID].insert(LineNo);
3210static std::unique_ptr<FilesToLineNumsMap>
3212 auto ExecutedLines = std::make_unique<FilesToLineNumsMap>();
3221 const Decl*
D = CE->getCalleeContext()->getDecl();
3232 if (
const auto *RS = dyn_cast_or_null<ReturnStmt>(
P)) {
3237 if (isa_and_nonnull<SwitchCase, LabelStmt>(
P))
3243 return ExecutedLines;
3246std::unique_ptr<DiagnosticForConsumerMapTy>
3250 auto *basicReport = cast<BasicBugReport>(exampleReport);
3251 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
3252 for (
auto *Consumer : consumers)
3268 "The call piece should not be in a header file.");
3280 if (
auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(
Path.back().get()))
3288 if (PD.
path.empty())
3297 if (
auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
3304 const auto *ND = dyn_cast<NamedDecl>(CP->
getCallee());
3307 llvm::raw_svector_ostream os(buf);
3308 os <<
" (within a call to '" << ND->getDeclName() <<
"')";
3323std::unique_ptr<DiagnosticForConsumerMapTy>
3324PathSensitiveBugReporter::generateDiagnosticForConsumerMap(
3327 std::vector<BasicBugReport *> BasicBugReports;
3328 std::vector<PathSensitiveBugReport *> PathSensitiveBugReports;
3329 if (isa<BasicBugReport>(exampleReport))
3331 consumers, bugReports);
3337 assert(!bugReports.empty());
3338 MaxBugClassSize.updateMax(bugReports.size());
3345 consumers, convertedArrayOfReports);
3350 MaxValidBugClassSize.updateMax(bugReports.size());
3355 for (
auto const &
P : *Out)
3356 if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.
AnalyzeAll)
3369 Loc, Ranges, Fixits);
3374 StringRef name, StringRef category,
3379 BugType *BT = getBugTypeForName(CheckName, name, category);
3380 auto R = std::make_unique<BasicBugReport>(*BT, str,
Loc);
3381 R->setDeclWithIssue(DeclWithIssue);
3382 for (
const auto &SR : Ranges)
3384 for (
const auto &FH : Fixits)
3390 StringRef name, StringRef category) {
3392 llvm::raw_svector_ostream(fullDesc) << CheckName.
getName() <<
":" << name
3394 std::unique_ptr<BugType> &BT = StrBugTypes[fullDesc];
3396 BT = std::make_unique<BugType>(CheckName, name, category);
BoundNodesTreeBuilder Nodes
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
static void dropFunctionEntryEdge(const PathDiagnosticConstruct &C, PathPieces &Path)
Drop the very first edge in a path, which should be a function entry edge.
constexpr llvm::StringLiteral StrLoopRangeEmpty
static PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S, const LocationContext *LC, bool allowNestedContexts=false)
static std::unique_ptr< FilesToLineNumsMap > findExecutedLines(const SourceManager &SM, const ExplodedNode *N)
static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond)
static void updateExecutedLinesWithDiagnosticPieces(PathDiagnostic &PD)
Populate executes lines with lines containing at least one diagnostics.
static void removeRedundantMsgs(PathPieces &path)
An optimization pass over PathPieces that removes redundant diagnostics generated by both ConditionBR...
constexpr llvm::StringLiteral StrLoopCollectionEmpty
static void adjustCallLocations(PathPieces &Pieces, PathDiagnosticLocation *LastCallLocation=nullptr)
Recursively scan through a path and make sure that all call pieces have valid locations.
static void removeIdenticalEvents(PathPieces &path)
static const Stmt * getTerminatorCondition(const CFGBlock *B)
A customized wrapper for CFGBlock::getTerminatorCondition() which returns the element for ObjCForColl...
static std::unique_ptr< VisitorsDiagnosticsTy > generateVisitorsDiagnostics(PathSensitiveBugReport *R, const ExplodedNode *ErrorNode, BugReporterContext &BRC)
Generate notes from all visitors.
static bool removeUnneededCalls(const PathDiagnosticConstruct &C, PathPieces &pieces, const PathSensitiveBugReport *R, bool IsInteresting=false)
Recursively scan through a path and prune out calls and macros pieces that aren't needed.
static void populateExecutedLinesWithStmt(const Stmt *S, const SourceManager &SM, FilesToLineNumsMap &ExecutedLines)
static bool isJumpToFalseBranch(const BlockEdge *BE)
static std::optional< size_t > getLengthOnSingleLine(const SourceManager &SM, SourceRange Range)
Returns the number of bytes in the given (character-based) SourceRange.
static bool isLoop(const Stmt *Term)
static bool isContainedByStmt(const ParentMap &PM, const Stmt *S, const Stmt *SubS)
constexpr llvm::StringLiteral StrEnteringLoop
static void addEdgeToPath(PathPieces &path, PathDiagnosticLocation &PrevLoc, PathDiagnosticLocation NewLoc)
Adds a sanitized control-flow diagnostic edge to a path.
static bool isIncrementOrInitInForLoop(const Stmt *S, const Stmt *FL)
static std::unique_ptr< PathDiagnostic > generateEmptyDiagnosticForReport(const PathSensitiveBugReport *R, const SourceManager &SM, const Decl *AnalysisEntryPoint)
static void removeContextCycles(PathPieces &Path, const SourceManager &SM)
Eliminate two-edge cycles created by addContextEdges().
static bool lexicalContains(const ParentMap &PM, const Stmt *X, const Stmt *Y)
Return true if X is contained by Y.
static std::unique_ptr< PathDiagnostic > generateDiagnosticForBasicReport(const BasicBugReport *R, const Decl *AnalysisEntryPoint)
static void removePopUpNotes(PathPieces &Path)
Same logic as above to remove extra pieces.
STATISTIC(MaxBugClassSize, "The maximum number of bug reports in the same equivalence class")
static void insertToInterestingnessMap(llvm::DenseMap< T, bugreporter::TrackingKind > &InterestingnessMap, T Val, bugreporter::TrackingKind TKind)
constexpr llvm::StringLiteral StrLoopBodyZero
static const Stmt * getEnclosingParent(const Stmt *S, const ParentMap &PM)
static void removePunyEdges(PathPieces &path, const SourceManager &SM, const ParentMap &PM)
static const Stmt * getStmtParent(const Stmt *S, const ParentMap &PM)
static void CompactMacroExpandedPieces(PathPieces &path, const SourceManager &SM)
CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic object and collapses PathDi...
static void simplifySimpleBranches(PathPieces &pieces)
Move edges from a branch condition to a branch target when the condition is simple.
static void populateExecutedLinesWithFunctionSignature(const Decl *Signature, const SourceManager &SM, FilesToLineNumsMap &ExecutedLines)
Insert all lines participating in the function signature Signature into ExecutedLines.
static void resetDiagnosticLocationToMainFile(PathDiagnostic &PD)
static bool optimizeEdges(const PathDiagnosticConstruct &C, PathPieces &path, OptimizedCallsSet &OCS)
static bool hasImplicitBody(const Decl *D)
Returns true if the given decl has been implicitly given a body, either by the analyzer or by the com...
static PathDiagnosticCallPiece * getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, const SourceManager &SMgr)
llvm::DenseSet< const PathDiagnosticCallPiece * > OptimizedCallsSet
static void addContextEdges(PathPieces &pieces, const LocationContext *LC)
Adds synthetic edges from top-level statements to their subexpressions.
static LLVM_ATTRIBUTE_USED bool isDependency(const CheckerRegistryData &Registry, StringRef CheckerName)
static PathDiagnosticEventPiece * eventsDescribeSameCondition(PathDiagnosticEventPiece *X, PathDiagnosticEventPiece *Y)
static bool isInLoopBody(const ParentMap &PM, const Stmt *S, const Stmt *Term)
static void removeEdgesToDefaultInitializers(PathPieces &Pieces)
Remove edges in and out of C++ default initializer expressions.
static const Stmt * getStmtBeforeCond(const ParentMap &PM, const Stmt *Term, const ExplodedNode *N)
static void removePiecesWithInvalidLocations(PathPieces &Pieces)
Remove all pieces with invalid locations as these cannot be serialized.
static LLVM_ATTRIBUTE_USED bool isHidden(const CheckerRegistryData &Registry, StringRef CheckerName)
Defines the clang::Expr interface and subclasses for C++ expressions.
llvm::DenseSet< const void * > Visited
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Defines the Objective-C statement AST node classes.
SourceManager & getSourceManager()
AnalysisDeclContext contains the context data for the function, method or block under analysis.
static std::string getFunctionName(const Decl *D)
bool isBodyAutosynthesized() const
bool isBodyAutosynthesizedFromModelFile() const
Stores options for the analyzer from the command line.
const CFGBlock * getSrc() const
const CFGBlock * getDst() const
Represents a single basic block in a source-level CFG.
succ_iterator succ_begin()
Stmt * getTerminatorStmt()
const Stmt * getLoopTarget() const
Stmt * getTerminatorCondition(bool StripParens=true)
unsigned succ_size() const
CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for statement, represented as 'for (ra...
Represents a point when we begin processing an inlined call.
Represents a point when we finish the call exit sequence (for inlined call).
const StackFrameContext * getCalleeContext() const
Decl - This represents one declaration (or definition), e.g.
ASTContext & getASTContext() const LLVM_READONLY
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
virtual bool hasBody() const
Returns true if this Decl represents a declaration for a body of code, such as a function or method d...
SourceLocation getLocation() const
This represents one expression.
llvm::APSInt EvaluateKnownConstInt(const ASTContext &Ctx, SmallVectorImpl< PartialDiagnosticAt > *Diag=nullptr) const
EvaluateKnownConstInt - Call EvaluateAsRValue and return the folded integer.
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
ForStmt - This represents a 'for (init;cond;inc)' stmt.
A SourceLocation and its associated SourceManager.
IfStmt - This represents an if/then/else.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
const ParentMap & getParentMap() const
const StackFrameContext * getStackFrame() const
Represents Objective-C's collection statement.
bool isConsumedExpr(Expr *E) const
Stmt * getParent(Stmt *) const
Stmt * getParentIgnoreParens(Stmt *) const
Represents a point after we ran remove dead bindings AFTER processing the given statement.
Represents a program point just before an implicit call event.
std::optional< T > getAs() const
Convert to the specified ProgramPoint type, returning std::nullopt if this ProgramPoint is not of the...
const LocationContext * getLocationContext() const
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
SourceLocation getBegin() const
It represents a stack frame of the call stack (based on CallEvent).
const Stmt * getCallSite() const
Stmt - This represents one statement.
StmtClass getStmtClass() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
SourceLocation getBeginLoc() const LLVM_READONLY
WhileStmt - This represents a 'while' stmt.
static bool isInCodeFile(SourceLocation SL, const SourceManager &SM)
const Decl * getDeclWithIssue() const override
The smallest declaration that contains the bug location.
PathDiagnosticLocation getUniqueingLocation() const override
Get the location on which the report should be uniqued.
void Profile(llvm::FoldingSetNodeID &hash) const override
Reports are uniqued to ensure that we do not emit multiple diagnostics for each bug.
const Decl * getUniqueingDecl() const override
Get the declaration that corresponds to (usually contains) the uniqueing location.
This class provides an interface through which checkers can create individual bug reports.
llvm::ArrayRef< FixItHint > getFixits() const
void addRange(SourceRange R)
Add a range to a bug report.
SmallVector< SourceRange, 4 > Ranges
virtual PathDiagnosticLocation getLocation() const =0
The primary location of the bug report that points at the undesirable behavior in the code.
ArrayRef< std::shared_ptr< PathDiagnosticNotePiece > > getNotes()
void addFixItHint(const FixItHint &F)
Add a fix-it hint to the bug report.
StringRef getDescription() const
A verbose warning message that is appropriate for displaying next to the source code that introduces ...
const BugType & getBugType() const
StringRef getShortDescription(bool UseFallback=true) const
A short general warning message that is appropriate for displaying in the list of all reported bugs.
virtual ArrayRef< SourceRange > getRanges() const
Get the SourceRanges associated with the report.
static PathDiagnosticPieceRef getDefaultEndPath(const BugReporterContext &BRC, const ExplodedNode *N, const PathSensitiveBugReport &BR)
Generates the default final diagnostic piece.
virtual ~BugReporterVisitor()
virtual std::unique_ptr< DiagnosticForConsumerMapTy > generateDiagnosticForConsumerMap(BugReport *exampleReport, ArrayRef< PathDiagnosticConsumer * > consumers, ArrayRef< BugReport * > bugReports)
Generate the diagnostics for the given bug report.
void FlushReports()
Generate and flush diagnostics for all bug reports.
BugReporter(BugReporterData &d)
const SourceManager & getSourceManager()
const Decl * getAnalysisEntryPoint() const
Get the top-level entry point for the issue to be reported.
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=std::nullopt, ArrayRef< FixItHint > Fixits=std::nullopt)
const AnalyzerOptions & getAnalyzerOptions()
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
ArrayRef< PathDiagnosticConsumer * > getPathDiagnosticConsumers()
bool isSuppressed(const BugReport &)
Return true if the given bug report was explicitly suppressed by the user.
bool isSuppressOnSink() const
isSuppressOnSink - Returns true if bug reports associated with this bug type should be suppressed if ...
StringRef getCategory() const
StringRef getDescription() const
StringRef getCheckerName() const
CheckerNameRef getCheckerName() const
This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...
StringRef getName() const
Visitor that tries to report interesting diagnostics from conditions.
static bool isPieceMessageGeneric(const PathDiagnosticPiece *Piece)
static const char * getTag()
Return the tag associated with this visitor.
bool isValid() const =delete
std::unique_ptr< ExplodedGraph > trim(ArrayRef< const NodeTy * > Nodes, InterExplodedGraphMap *ForwardMap=nullptr, InterExplodedGraphMap *InverseMap=nullptr) const
Creates a trimmed version of the graph that only contains paths leading to the given nodes.
const CFGBlock * getCFGBlock() const
const ProgramStateRef & getState() const
pred_iterator pred_begin()
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
const Stmt * getPreviousStmtForDiagnostics() const
Find the statement that was executed immediately before this node.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
void addPredecessor(ExplodedNode *V, ExplodedGraph &G)
addPredeccessor - Adds a predecessor to the current node, and in tandem add this node as a successor ...
const Stmt * getNextStmtForDiagnostics() const
Find the next statement that was executed on this node's execution path.
const ParentMap & getParentMap() const
SVal getSVal(const Stmt *S) const
Get the value of an arbitrary expression at this node.
const LocationContext * getLocationContext() const
std::optional< T > getLocationAs() const &
const Stmt * getCurrentOrPreviousStmtForDiagnostics() const
Find the statement that was executed at or immediately before this node.
ExplodedNode * getFirstPred()
const ExplodedNode *const * const_succ_iterator
ProgramStateManager & getStateManager()
ExplodedGraph & getGraph()
Suppress reports that might lead to known false positives.
MemRegion - The root abstract class for all memory regions.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
Prints path notes when a message is sent to a nil receiver.
PathDiagnosticLocation getLocation() const override
PathDiagnosticLocation callEnter
void setCallStackMessage(StringRef st)
bool hasCallStackMessage()
const Decl * getCallee() const
static std::shared_ptr< PathDiagnosticCallPiece > construct(const CallExitEnd &CE, const SourceManager &SM)
const Decl * getCaller() const
PathDiagnosticLocation callEnterWithin
virtual bool supportsLogicalOpControlFlow() const
bool shouldAddPathEdges() const
void HandlePathDiagnostic(std::unique_ptr< PathDiagnostic > D)
bool shouldAddControlNotes() const
bool shouldGenerateDiagnostics() const
PathDiagnosticLocation getStartLocation() const
void setStartLocation(const PathDiagnosticLocation &L)
PathDiagnosticLocation getEndLocation() const
static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME, const SourceManager &SM)
For member expressions, return the location of the '.
const Stmt * asStmt() const
void Profile(llvm::FoldingSetNodeID &ID) const
const SourceManager & getManager() const
static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, const SourceManager &SM)
Create the location for the operator of the binary expression.
static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS, const SourceManager &SM)
Create a location for the end of the compound statement.
static SourceLocation getValidSourceLocation(const Stmt *S, LocationOrAnalysisDeclContext LAC, bool UseEndOfStatement=false)
Construct a source location that corresponds to either the beginning or the end of the given statemen...
static PathDiagnosticLocation createEnd(const Stmt *S, const SourceManager &SM, const LocationOrAnalysisDeclContext LAC)
Create a location for the end of the statement.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
FullSourceLoc asLocation() const
static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, const SourceManager &SM)
Constructs a location for the end of the enclosing declaration body.
const Stmt * getStmtOrNull() const
static PathDiagnosticLocation createSingleLocation(const PathDiagnosticLocation &PDL)
Convert the given location into a single kind location.
ArrayRef< SourceRange > getRanges() const
Return the SourceRanges associated with this PathDiagnosticPiece.
virtual PathDiagnosticLocation getLocation() const =0
void setAsLastInMainSourceFile()
const void * getTag() const
Return the opaque tag (if any) on the PathDiagnosticPiece.
StringRef getString() const
PathDiagnosticLocation getLocation() const override
PathDiagnostic - PathDiagnostic objects represent a single path-sensitive diagnostic.
void setDeclWithIssue(const Decl *D)
void appendToDesc(StringRef S)
void setLocation(PathDiagnosticLocation NewLoc)
const FilesToLineNumsMap & getExecutedLines() const
PathPieces flatten(bool ShouldFlattenMacros) const
llvm::SmallSet< const LocationContext *, 2 > InterestingLocationContexts
A set of location contexts that correspoind to call sites which should be considered "interesting".
void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind=bugreporter::TrackingKind::Thorough)
Marks a symbol as interesting.
PathDiagnosticLocation getUniqueingLocation() const override
Get the location on which the report should be uniqued.
VisitorList Callbacks
A set of custom visitors which generate "event" diagnostics at interesting points in the path.
const Stmt * getStmt() const
PathDiagnosticLocation getLocation() const override
The primary location of the bug report that points at the undesirable behavior in the code.
const Decl * getDeclWithIssue() const override
The smallest declaration that contains the bug location.
bool shouldPrunePath() const
Indicates whether or not any path pruning should take place when generating a PathDiagnostic from thi...
ArrayRef< SourceRange > getRanges() const override
Get the SourceRanges associated with the report.
llvm::DenseMap< SymbolRef, bugreporter::TrackingKind > InterestingSymbols
Profile to identify equivalent bug reports for error report coalescing.
const Decl * getUniqueingDecl() const override
Get the declaration containing the uniqueing location.
const ExplodedNode * getErrorNode() const
PathSensitiveBugReport(const BugType &bt, StringRef desc, const ExplodedNode *errorNode)
const ExplodedNode * ErrorNode
The ExplodedGraph node against which the report was thrown.
void markInvalid(const void *Tag, const void *Data)
Marks the current report as invalid, meaning that it is probably a false positive and should not be r...
void Profile(llvm::FoldingSetNodeID &hash) const override
Profile to identify equivalent bug reports for error report coalescing.
void clearVisitors()
Remove all visitors attached to this bug report.
void addVisitor(std::unique_ptr< BugReporterVisitor > visitor)
Add custom or predefined bug report visitors to this report.
bool isValid() const
Returns whether or not this report should be considered valid.
std::optional< bugreporter::TrackingKind > getInterestingnessKind(SymbolRef sym) const
void markNotInteresting(SymbolRef sym)
llvm::DenseMap< const MemRegion *, bugreporter::TrackingKind > InterestingRegions
A (stack of) set of regions that are registered with this report as being "interesting",...
bool isInteresting(SymbolRef sym) const
const SourceRange ErrorNodeRange
The range that corresponds to ErrorNode's program point.
llvm::FoldingSet< BugReporterVisitor > CallbacksSet
Used for ensuring the visitors are only added once.
GRBugReporter is used for generating path-sensitive reports.
const ExplodedGraph & getGraph() const
getGraph - Get the exploded graph created by the analysis engine for the analyzed method or function.
std::unique_ptr< DiagnosticForConsumerMapTy > generatePathDiagnostics(ArrayRef< PathDiagnosticConsumer * > consumers, ArrayRef< PathSensitiveBugReport * > &bugReports)
bugReports A set of bug reports within a single equivalence class
void emitReport(std::unique_ptr< BugReport > R) override
Add the given report to the set of reports tracked by BugReporter.
ProgramStateManager & getStateManager() const
getStateManager - Return the state manager used by the analysis engine.
A Range represents the closed range [from, to].
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const
If this SVal is a location and wraps a symbol, return that SymbolRef.
std::string getMessage(const ExplodedNode *N) override
Search the call expression for the symbol Sym and dispatch the 'getMessageForX()' methods to construc...
virtual std::string getMessageForSymbolNotFound()
virtual std::string getMessageForReturn(const CallExpr *CallExpr)
virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex)
Produces the message of the following form: 'Msg via Nth parameter'.
virtual ~StackHintGenerator()=0
The visitor detects NoteTags and displays the event notes they contain.
static const char * getTag()
Return the tag associated with this visitor.
The oracle will decide if a report should be accepted or rejected based on the results of the Z3 solv...
The bug visitor will walk all the nodes in a path and collect all the constraints.
TrackingKind
Specifies the type of tracking for an expression.
@ Thorough
Default tracking kind – specifies that as much information should be gathered about the tracked expre...
@ Condition
Specifies that a more moderate tracking should be used for the expression value.
llvm::DenseMap< const ExplodedNode *, const ExplodedNode * > InterExplodedGraphMap
std::map< FileID, std::set< unsigned > > FilesToLineNumsMap
File IDs mapped to sets of line numbers.
@ CF
Indicates that the tracked object is a CF object.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
bool EQ(InterpState &S, CodePtr OpPC)
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
@ Result
The result type of a method or function.
const FunctionProtoType * T
Diagnostic wrappers for TextAPI types for error reporting.
llvm::SmallVector< std::pair< StringRef, StringRef >, 0 > Dependencies